using System;
using System.Diagnostics;
using System.Text;
using Community.CsharpSqlite.Entity;

namespace Community.CsharpSqlite
{
    using DbPage = PgHdr;

    public partial class BTreeHelper
    {
        /*
    ** 2004 April 6
    **
    ** The author disclaims copyright to this source code.  In place of
    ** a legal notice, here is a blessing:
    **
    **    May you do good and not evil.
    **    May you find forgiveness for yourself and forgive others.
    **    May you share freely, never taking more than you give.
    **
    ** This file implements a external (disk-based) database using BTrees.
    ** See the header comment on "btreeInt.h" for additional information.
    ** Including a description of file format and an overview of operation.
    *************************************************************************
    **  Included in SQLite3 port to C#-SQLite;  2008 Noah B Hart
    **  C#-SQLite is an independent reimplementation of the SQLite software library
    **
    **  SQLITE_SOURCE_ID: 2010-03-09 19:31:43 4ae453ea7be69018d8c16eb8dabe05617397dc4d
    **
    **  $Header: Community.CsharpSqlite/src/btree_c.cs,v 6604176a7dbe 2010/03/12 23:35:36 Noah $
    *************************************************************************
    */
        //#include "btreeInt.h"

        /*
    ** The header string that appears at the beginning of every
    ** SQLite database.
    */
        private static string zMagicHeader = SQLITE_FILE_HEADER;

        /*
    ** Set this global variable to 1 to enable tracing using the TRACE
    ** macro.
    */
#if TRACE 
static bool sqlite3BtreeTrace=false;  /* True to enable tracing */
//# define TRACE(X)  if(sqlite3BtreeTrace){Custom.Printf X;fflush(stdout);}
static void TRACE(string X, params object[] ap) { if (sqlite3BtreeTrace)  Custom.Printf(X, ap); }
#else
        //# define TRACE(X)
        private static void TRACE(string X, params object[] ap)
        {
        }
#endif


#if !SQLITE_OMIT_SHARED_CACHE
/*
** A list of BtShared objects that are eligible for participation
** in shared cache.  This variable has file scope during normal builds,
** but the test harness needs to access it so we make it global for
** test builds.
**
** Access to this variable is protected by MutexType.SQLITE_MUTEX_STATIC_MASTER.
*/
#if SQLITE_TEST
BtShared *SQLITE_WSD sqlite3SharedCacheList = 0;
#else
static BtShared *SQLITE_WSD sqlite3SharedCacheList = 0;
#endif
#endif
        //* SQLITE_OMIT_SHARED_CACHE */

#if !SQLITE_OMIT_SHARED_CACHE
/*
** Enable or disable the shared pager and schema features.
**
** This routine has no effect on existing database connections.
** The shared cache setting effects only future calls to
** sqlite3_open(), sqlite3_open16(), or sqlite3_open_v2().
*/
int sqlite3_enable_shared_cache(int enable){
Global.Config.sharedCacheEnabled = enable;
return StatusCode.SQLITE_OK;
}
#endif


#if SQLITE_OMIT_SHARED_CACHE
        /*
** The functions querySharedCacheTableLock(), setSharedCacheTableLock(),
** and clearAllSharedCacheTableLocks()
** manipulate entries in the BtShared.pLock linked list used to store
** shared-cache table level locks. If the library is compiled with the
** shared-cache feature disabled, then there is only ever one user
** of each BtShared structure and so this locking is not necessary.
** So define the lock related functions as no-ops.
*/
        //#define querySharedCacheTableLock(a,b,c) StatusCode.SQLITE_OK
        private static int querySharedCacheTableLock(Btree p, uint iTab, byte eLock)
        {
            return StatusCode.SQLITE_OK;
        }

        //#define setSharedCacheTableLock(a,b,c) StatusCode.SQLITE_OK
        //#define clearAllSharedCacheTableLocks(a)
        private static void clearAllSharedCacheTableLocks(Btree a)
        {
        }

        //#define downgradeAllSharedCacheTableLocks(a)
        private static void downgradeAllSharedCacheTableLocks(Btree a)
        {
        }

        //#define hasSharedCacheTableLock(a,b,c,d) 1
        private static bool hasSharedCacheTableLock(Btree a, uint b, int c, int d)
        {
            return true;
        }

        //#define hasReadConflicts(a, b) 0
        private static bool hasReadConflicts(Btree a, uint b)
        {
            return false;
        }
#endif

#if !SQLITE_OMIT_SHARED_CACHE

#if SQLITE_DEBUG
/*
**** This function is only used as part of an assert() statement. ***
**
** Check to see if pBtree holds the required locks to read or write to the 
** table with root page iRoot.   Return 1 if it does and 0 if not.
**
** For example, when writing to a table with root-page iRoot via 
** Btree connection pBtree:
**
**    assert( hasSharedCacheTableLock(pBtree, iRoot, 0, LockType.WRITE_LOCK) );
**
** When writing to an index that resides in a sharable database, the 
** caller should have first obtained a lock specifying the root page of
** the corresponding table. This makes things a bit more complicated,
** as this module treats each table as a separate structure. To determine
** the table corresponding to the index being written, this
** function has to search through the database schema.
**
** Instead of a lock on the table/index rooted at page iRoot, the caller may
** hold a write-lock on the schema table (root page 1). This is also
** acceptable.
*/
static int hasSharedCacheTableLock(
Btree pBtree,         /* Handle that must hold lock */
uint iRoot,            /* Root page of b-tree */
int isIndex,           /* True if iRoot is the root of an index b-tree */
int eLockType          /* Required lock type (LockType.READ_LOCK or LockType.WRITE_LOCK) */
){
Schema pSchema = (Schema *)pBtree.pBt.pSchema;
uint iTab = 0;
BtLock pLock;

  /* If this database is not shareable, or if the client is reading
  ** and has the read-uncommitted flag set, then no lock is required. 
  ** Return true immediately.
  */
if( (pBtree.sharable==null)
|| (eLockType==LockType.READ_LOCK && (pBtree.db.flags & Flag.SQLITE_ReadUncommitted))
){
return 1;
}

  /* If the client is reading  or writing an index and the schema is
  ** not loaded, then it is too difficult to actually check to see if
  ** the correct locks are held.  So do not bother - just return true.
  ** This case does not come up very often anyhow.
  */
  if( isIndex && (!pSchema || (pSchema->flags&DBSchemaFlag.DB_SchemaLoaded)==0) ){
    return 1;
  }

/* Figure out the root-page that the lock should be held on. For table
** b-trees, this is just the root page of the b-tree being read or
** written. For index b-trees, it is the root page of the associated
** table.  */
if( isIndex ){
HashElem p;
for(p=HashHelper.HashFirst(pSchema.idxHash); p!=null; p=HashHelper.HashNext(p)){
Index pIdx = (Index *)HashHelper.HashData(p);
if( pIdx.tnum==(int)iRoot ){
iTab = pIdx.pTable.tnum;
}
}
}else{
iTab = iRoot;
}

/* Search for the required lock. Either a write-lock on root-page iTab, a
** write-lock on the schema table, or (if the client is reading) a
** read-lock on iTab will suffice. Return 1 if any of these are found.  */
for(pLock=pBtree.pBt.pLock; pLock; pLock=pLock.pNext){
if( pLock.pBtree==pBtree
&& (pLock.iTable==iTab || (pLock.eLock==LockType.WRITE_LOCK && pLock.iTable==1))
&& pLock.eLock>=eLockType
){
return 1;
}
}

/* Failed to find the required lock. */
return 0;
}

#endif //* SQLITE_DEBUG */

#if SQLITE_DEBUG
/*
** This function may be used as part of assert() statements only. ****
**
** Return true if it would be illegal for pBtree to write into the
** table or index rooted at iRoot because other shared connections are
** simultaneously reading that same table or index.
**
** It is illegal for pBtree to write if some other Btree object that
** shares the same BtShared object is currently reading or writing
** the iRoot table.  Except, if the other Btree object has the
** read-uncommitted flag set, then it is OK for the other object to
** have a read cursor.
**
** For example, before writing to any part of the table or index
** rooted at page iRoot, one should call:
**
**    assert( !hasReadConflicts(pBtree, iRoot) );
*/
static int hasReadConflicts(Btree pBtree, uint iRoot){
BtCursor p;
for(p=pBtree.pBt.pCursor; p!=null; p=p.pNext){
if( p.pgnoRoot==iRoot
&& p.pBtree!=pBtree
&& 0==(p.pBtree.db.flags & Flag.SQLITE_ReadUncommitted)
){
return 1;
}
}
return 0;
}
#endif    //* #if SQLITE_DEBUG */

/*
** Query to see if Btree handle p may obtain a lock of type eLock
** (LockType.READ_LOCK or LockType.WRITE_LOCK) on the table with root-page iTab. Return
** StatusCode.SQLITE_OK if the lock may be obtained (by calling
** setSharedCacheTableLock()), or StatusCode.SQLITE_LOCKED if not.
*/
static int querySharedCacheTableLock(Btree p, uint iTab, byte eLock){
BtShared pBt = p.pBt;
BtLock pIter;

Debug.Assert( sqlite3BtreeHoldsMutex(p) );
Debug.Assert( eLock==LockType.READ_LOCK || eLock==LockType.WRITE_LOCK );
Debug.Assert( p.db!=null );
Debug.Assert( !(p.db.flags&Flag.SQLITE_ReadUncommitted)||eLock==LockType.WRITE_LOCK||iTab==1 );

/* If requesting a write-lock, then the Btree must have an open write
** transaction on this file. And, obviously, for this to be so there
** must be an open write transaction on the file itself.
*/
Debug.Assert( eLock==LockType.READ_LOCK || (p==pBt.pWriter && p.inTrans==TransactionType.TRANS_WRITE) );
Debug.Assert( eLock==LockType.READ_LOCK || pBt.inTransaction==TransactionType.TRANS_WRITE );

/* This routine is a no-op if the shared-cache is not enabled */
if( !p.sharable ){
return StatusCode.SQLITE_OK;
}

/* If some other connection is holding an exclusive lock, the
** requested lock may not be obtained.
*/
if( pBt.pWriter!=p && pBt.isExclusive ){
sqlite3ConnectionBlocked(p.db, pBt.pWriter.db);
return ExtendedResultCode.SQLITE_LOCKED_SHAREDCACHE;
}

for(pIter=pBt.pLock; pIter; pIter=pIter.pNext){
/* The condition (pIter.eLock!=eLock) in the following if(...)
** statement is a simplification of:
**
**   (eLock==LockType.WRITE_LOCK || pIter.eLock==LockType.WRITE_LOCK)
**
** since we know that if eLock==LockType.WRITE_LOCK, then no other connection
** may hold a LockType.WRITE_LOCK on any table in this file (since there can
** only be a single writer).
*/
Debug.Assert( pIter.eLock==LockType.READ_LOCK || pIter.eLock==LockType.WRITE_LOCK );
Debug.Assert( eLock==LockType.READ_LOCK || pIter.pBtree==p || pIter.eLock==LockType.READ_LOCK);
if( pIter.pBtree!=p && pIter.iTable==iTab && pIter.eLock!=eLock ){
sqlite3ConnectionBlocked(p.db, pIter.pBtree.db);
if( eLock==LockType.WRITE_LOCK ){
Debug.Assert( p==pBt.pWriter );
pBt.isPending = 1;
}
return ExtendedResultCode.SQLITE_LOCKED_SHAREDCACHE;
}
}
return StatusCode.SQLITE_OK;
}
#endif
        //* !SQLITE_OMIT_SHARED_CACHE */

#if !SQLITE_OMIT_SHARED_CACHE
/*
** Add a lock on the table with root-page iTable to the shared-btree used
** by Btree handle p. Parameter eLock must be either LockType.READ_LOCK or 
** LockType.WRITE_LOCK.
**
** This function assumes the following:
**
**   (a) The specified Btree object p is connected to a sharable
**       database (one with the BtShared.sharable flag set), and
**
**   (b) No other Btree objects hold a lock that conflicts
**       with the requested lock (i.e. querySharedCacheTableLock() has
**       already been called and returned StatusCode.SQLITE_OK).
**
** StatusCode.SQLITE_OK is returned if the lock is added successfully. StatusCode.SQLITE_NOMEM 
** is returned if a malloc attempt fails.
*/
static int setSharedCacheTableLock(Btree p, uint iTable, byte eLock){
BtShared pBt = p.pBt;
BtLock pLock = 0;
BtLock pIter;

Debug.Assert( sqlite3BtreeHoldsMutex(p) );
Debug.Assert( eLock==LockType.READ_LOCK || eLock==LockType.WRITE_LOCK );
Debug.Assert( p.db!=null );

/* A connection with the read-uncommitted flag set will never try to
** obtain a read-lock using this function. The only read-lock obtained
** by a connection in read-uncommitted mode is on the sqlite_master
** table, and that lock is obtained in BtreeBeginTrans().  */
Debug.Assert( 0==(p.db.flags&Flag.SQLITE_ReadUncommitted) || eLock==LockType.WRITE_LOCK );

/* This function should only be called on a sharable b-tree after it
** has been determined that no other b-tree holds a conflicting lock.  */
Debug.Assert( p.sharable );
Debug.Assert( StatusCode.SQLITE_OK==querySharedCacheTableLock(p, iTable, eLock) );

/* First search the list for an existing lock on this table. */
for(pIter=pBt.pLock; pIter; pIter=pIter.pNext){
if( pIter.iTable==iTable && pIter.pBtree==p ){
pLock = pIter;
break;
}
}

/* If the above search did not find a BtLock struct associating Btree p
** with table iTable, allocate one and link it into the list.
*/
if( !pLock ){
pLock = (BtLock *)Malloc.MallocZero(sizeof(BtLock));
if( !pLock ){
return StatusCode.SQLITE_NOMEM;
}
pLock.iTable = iTable;
pLock.pBtree = p;
pLock.pNext = pBt.pLock;
pBt.pLock = pLock;
}

/* Set the BtLock.eLock variable to the maximum of the current lock
** and the requested lock. This means if a write-lock was already held
** and a read-lock requested, we don't incorrectly downgrade the lock.
*/
Debug.Assert( LockType.WRITE_LOCK>LockType.READ_LOCK );
if( eLock>pLock.eLock ){
pLock.eLock = eLock;
}

return StatusCode.SQLITE_OK;
}
#endif
        //* !SQLITE_OMIT_SHARED_CACHE */

#if !SQLITE_OMIT_SHARED_CACHE
/*
** Release all the table locks (locks obtained via calls to
** the setSharedCacheTableLock() procedure) held by Btree object p.
**
** This function assumes that Btree p has an open read or write 
** transaction. If it does not, then the BtShared.isPending variable
** may be incorrectly cleared.
*/
static void clearAllSharedCacheTableLocks(Btree p){
BtShared pBt = p.pBt;
BtLock **ppIter = &pBt.pLock;

Debug.Assert( sqlite3BtreeHoldsMutex(p) );
Debug.Assert( p.sharable || 0==*ppIter );
Debug.Assert( p.inTrans>0 );

while( ppIter ){
BtLock pLock = ppIter;
Debug.Assert( pBt.isExclusive==null || pBt.pWriter==pLock.pBtree );
Debug.Assert( pLock.pBtree.inTrans>=pLock.eLock );
if( pLock.pBtree==p ){
ppIter = pLock.pNext;
Debug.Assert( pLock.iTable!=1 || pLock==&p.lock );
if( pLock.iTable!=1 ){
pLock=null;//Malloc.sqlite3_free(ref pLock);
}
}else{
ppIter = &pLock.pNext;
}
}

Debug.Assert( pBt.isPending==null || pBt.pWriter );
if( pBt.pWriter==p ){
pBt.pWriter = 0;
pBt.isExclusive = 0;
pBt.isPending = 0;
}else if( pBt.nTransaction==2 ){
/* This function is called when Btree p is concluding its 
** transaction. If there currently exists a writer, and p is not
** that writer, then the number of locks held by connections other
** than the writer must be about to drop to zero. In this case
** set the isPending flag to 0.
**
** If there is not currently a writer, then BtShared.isPending must
** be zero already. So this next line is harmless in that case.
*/
pBt.isPending = 0;
}
}

/*
** This function changes all write-locks held by Btree p into read-locks.
*/
static void downgradeAllSharedCacheTableLocks(Btree p){
BtShared pBt = p.pBt;
if( pBt.pWriter==p ){
BtLock pLock;
pBt.pWriter = 0;
pBt.isExclusive = 0;
pBt.isPending = 0;
for(pLock=pBt.pLock; pLock; pLock=pLock.pNext){
Debug.Assert( pLock.eLock==LockType.READ_LOCK || pLock.pBtree==p );
pLock.eLock = LockType.READ_LOCK;
}
}
}

#endif
        //* SQLITE_OMIT_SHARED_CACHE */

        //static void releasePage(MemPage pPage);  /* Forward reference */

        /*
    ***** This routine is used inside of assert() only ****
    **
    ** Verify that the cursor holds the mutex on its BtShared
    */
#if SQLITE_DEBUG
        private static bool cursorHoldsMutex(BtCursor p)
        {
            return MutexHelper.MutexHeld(p.pBt.mutex);
        }
#else
static bool cursorHoldsMutex(BtCursor p) { return true; }
#endif


#if !SQLITE_OMIT_INCRBLOB
/*
** Invalidate the overflow page-list cache for cursor pCur, if any.
*/
static void invalidateOverflowCache(BtCursor pCur){
Debug.Assert( cursorHoldsMutex(pCur) );
//Malloc.sqlite3_free(ref pCur.aOverflow);
pCur.aOverflow = null;
}

/*
** Invalidate the overflow page-list cache for all cursors opened
** on the shared btree structure pBt.
*/
static void invalidateAllOverflowCache(BtShared pBt){
BtCursor p;
Debug.Assert( MutexHelper.MutexHeld(pBt.mutex) );
for(p=pBt.pCursor; p!=null; p=p.pNext){
invalidateOverflowCache(p);
}
}

/*
** This function is called before modifying the contents of a table
** to invalidate any incrblob cursors that are open on the
** row or one of the rows being modified.
**
** If argument isClearTable is true, then the entire contents of the
** table is about to be deleted. In this case invalidate all incrblob
** cursors open on any row within the table with root-page pgnoRoot.
**
** Otherwise, if argument isClearTable is false, then the row with
** rowid iRow is being replaced or deleted. In this case invalidate
** only those incrblob cursors open on that specific row.
*/
static void invalidateIncrblobCursors(
Btree pBtree,          /* The database file to check */
long iRow,               /* The rowid that might be changing */
int isClearTable        /* True if all rows are being deleted */
){
BtCursor p;
BtShared pBt = pBtree.pBt;
Debug.Assert( sqlite3BtreeHoldsMutex(pBtree) );
for(p=pBt.pCursor; p!=null; p=p.pNext){
if( p.isIncrblobHandle && (isClearTable || p.info.nKey==iRow) ){
p.eState = CursorFlag.CURSOR_INVALID;
}
}
}

#else
        /* Stub functions when INCRBLOB is omitted */
        //#define invalidateOverflowCache(x)
        private static void invalidateOverflowCache(BtCursor pCur)
        {
        }

        //#define invalidateAllOverflowCache(x)
        private static void invalidateAllOverflowCache(BtShared pBt)
        {
        }

        //#define invalidateIncrblobCursors(x,y,z)
        private static void invalidateIncrblobCursors(Btree x, long y, int z)
        {
        }
#endif
        //* SQLITE_OMIT_INCRBLOB */

        /*
** Set bit pgno of the BtShared.pHasContent bitvec. This is called
** when a page that previously contained data becomes a free-list leaf
** page.
**
** The BtShared.pHasContent bitvec exists to work around an obscure
** bug caused by the interaction of two useful IO optimizations surrounding
** free-list leaf pages:
**
**   1) When all data is deleted from a page and the page becomes
**      a free-list leaf page, the page is not written to the database
**      (as free-list leaf pages contain no meaningful data). Sometimes
**      such a page is not even journalled (as it will not be modified,
**      why bother journalling it?).
**
**   2) When a free-list leaf page is reused, its content is not read
**      from the database or written to the journal file (why should it
**      be, if it is not at all meaningful?).
**
** By themselves, these optimizations work fine and provide a handy
** performance boost to bulk delete or insert operations. However, if
** a page is moved to the free-list and then reused within the same
** transaction, a problem comes up. If the page is not journalled when
** it is moved to the free-list and it is also not journalled when it
** is extracted from the free-list and reused, then the original data
** may be lost. In the event of a rollback, it may not be possible
** to restore the database to its original configuration.
**
** The solution is the BtShared.pHasContent bitvec. Whenever a page is
** moved to become a free-list leaf page, the corresponding bit is
** set in the bitvec. Whenever a leaf page is extracted from the free-list,
** optimization 2 above is omitted if the corresponding bit is already
** set in BtShared.pHasContent. The contents of the bitvec are cleared
** at the end of every transaction.
*/

        private static int btreeSetHasContent(BtShared pBt, uint pgno)
        {
            int rc = StatusCode.SQLITE_OK;
            if (null == pBt.pHasContent)
            {
                int nPage = 100;
                sqlite3PagerPagecount(pBt.pPager, ref nPage);
                /* If sqlite3PagerPagecount() fails there is no harm because the
        ** nPage variable is unchanged from its default value of 100 */
                pBt.pHasContent = sqlite3BitvecCreate((uint) nPage);
                if (null == pBt.pHasContent)
                {
                    rc = StatusCode.SQLITE_NOMEM;
                }
            }
            if (rc == StatusCode.SQLITE_OK && pgno <= sqlite3BitvecSize(pBt.pHasContent))
            {
                rc = sqlite3BitvecSet(pBt.pHasContent, pgno);
            }
            return rc;
        }

        /*
    ** Query the BtShared.pHasContent vector.
    **
    ** This function is called when a free-list leaf page is removed from the
    ** free-list for reuse. It returns false if it is safe to retrieve the
    ** page from the pager layer with the 'no-content' flag set. True otherwise.
    */

        private static bool btreeGetHasContent(BtShared pBt, uint pgno)
        {
            Bitvec p = pBt.pHasContent;
            return (p != null && (pgno > sqlite3BitvecSize(p) || sqlite3BitvecTest(p, pgno) != 0));
        }

        /*
    ** Clear (destroy) the BtShared.pHasContent bitvec. This should be
    ** invoked at the conclusion of each write-transaction.
    */

        private static void btreeClearHasContent(BtShared pBt)
        {
            sqlite3BitvecDestroy(ref pBt.pHasContent);
            pBt.pHasContent = null;
        }

        /*
    ** Save the current cursor position in the variables BtCursor.nKey
    ** and BtCursor.pKey. The cursor's state is set to CursorFlag.CURSOR_REQUIRESEEK.
    **
    ** The caller must ensure that the cursor is valid (has eState==CursorFlag.CURSOR_VALID)
    ** prior to calling this routine.
    */

        private static int saveCursorPosition(BtCursor pCur)
        {
            int rc;

            Debug.Assert(CursorFlag.CURSOR_VALID == pCur.eState);
            Debug.Assert(null == pCur.pKey);
            Debug.Assert(cursorHoldsMutex(pCur));

            rc = BtreeKeySize(pCur, ref pCur.nKey);
            Debug.Assert(rc == StatusCode.SQLITE_OK); /* KeySize() cannot fail */

            /* If this is an intKey table, then the above call to BtreeKeySize()
      ** stores the integer key in pCur.nKey. In this case this value is
      ** all that is required. Otherwise, if pCur is not open on an intKey
      ** table, then malloc space for and store the pCur.nKey bytes of key
      ** data.
      */
            if (0 == pCur.apPage[0].intKey)
            {
                byte[] pKey = Malloc.sqlite3Malloc((int) pCur.nKey);
                //if( pKey !=null){
                rc = BtreeKey(pCur, 0, (uint) pCur.nKey, pKey);
                if (rc == StatusCode.SQLITE_OK)
                {
                    pCur.pKey = pKey;
                }
                //else{
                //  Malloc.sqlite3_free(ref pKey);
                //}
                //}else{
                //  rc = StatusCode.SQLITE_NOMEM;
                //}
            }
            Debug.Assert(0 == pCur.apPage[0].intKey || null == pCur.pKey);

            if (rc == StatusCode.SQLITE_OK)
            {
                int i;
                for (i = 0; i <= pCur.iPage; i++)
                {
                    releasePage(pCur.apPage[i]);
                    pCur.apPage[i] = null;
                }
                pCur.iPage = -1;
                pCur.eState = CursorFlag.CURSOR_REQUIRESEEK;
            }

            invalidateOverflowCache(pCur);
            return rc;
        }

        /*
    ** Save the positions of all cursors (except pExcept) that are open on
    ** the table  with root-page iRoot. Usually, this is called just before cursor
    ** pExcept is used to modify the table (BtreeDelete() or BtreeInsert()).
    */

        private static int saveAllCursors(BtShared pBt, uint iRoot, BtCursor pExcept)
        {
            BtCursor p;
            Debug.Assert(MutexHelper.MutexHeld(pBt.mutex));
            Debug.Assert(pExcept == null || pExcept.pBt == pBt);
            for (p = pBt.pCursor; p != null; p = p.pNext)
            {
                if (p != pExcept && (0 == iRoot || p.pgnoRoot == iRoot) &&
                    p.eState == CursorFlag.CURSOR_VALID)
                {
                    int rc = saveCursorPosition(p);
                    if (StatusCode.SQLITE_OK != rc)
                    {
                        return rc;
                    }
                }
            }
            return StatusCode.SQLITE_OK;
        }

        /*
    ** Clear the current cursor position.
    */

        private static void sqlite3BtreeClearCursor(BtCursor pCur)
        {
            Debug.Assert(cursorHoldsMutex(pCur));
            Malloc.sqlite3_free(ref pCur.pKey);
            pCur.eState = CursorFlag.CURSOR_INVALID;
        }

        /*
    ** In this version of BtreeMoveto, pKey is a packed index record
    ** such as is generated by the OPCode.OP_MakeRecord opcode.  Unpack the
    ** record and then call BtreeMovetoUnpacked() to do the work.
    */

        private static int btreeMoveto(
            BtCursor pCur, /* Cursor open on the btree to be searched */
            byte[] pKey, /* Packed key if the btree is an index */
            long nKey, /* Integer key for tables.  Size of pKey for indices */
            int bias, /* Bias search to the high end */
            ref int pRes /* Write search results here */
            )
        {
            int rc; /* Status code */
            UnpackedRecord pIdxKey; /* Unpacked index key */
            var aSpace = new UnpackedRecord(); //char aSpace[150]; /* Temp space for pIdxKey - to avoid a malloc */

            if (pKey != null)
            {
                Debug.Assert(nKey == (int) nKey);
                pIdxKey = VdbeAux.VdbeRecordUnpack(pCur.pKeyInfo, (int) nKey, pKey,
                                                  aSpace, 16); //sizeof( aSpace ) );
                if (pIdxKey == null) return StatusCode.SQLITE_NOMEM;
            }
            else
            {
                pIdxKey = null;
            }
            rc = BtreeMovetoUnpacked(pCur, pIdxKey, nKey, bias != 0 ? 1 : 0, ref pRes);

            if (pKey != null)
            {
                VdbeAux.VdbeDeleteUnpackedRecord(pIdxKey);
            }
            return rc;
        }

        /*
    ** Restore the cursor to the position it was in (or as close to as possible)
    ** when saveCursorPosition() was called. Note that this call deletes the
    ** saved position info stored by saveCursorPosition(), so there can be
    ** at most one effective restoreCursorPosition() call after each
    ** saveCursorPosition().
    */

        private static int btreeRestoreCursorPosition(BtCursor pCur)
        {
            int rc;
            Debug.Assert(cursorHoldsMutex(pCur));
            Debug.Assert(pCur.eState >= CursorFlag.CURSOR_REQUIRESEEK);
            if (pCur.eState == CursorFlag.CURSOR_FAULT)
            {
                return pCur.skipNext;
            }
            pCur.eState = CursorFlag.CURSOR_INVALID;
            rc = btreeMoveto(pCur, pCur.pKey, pCur.nKey, 0, ref pCur.skipNext);
            if (rc == StatusCode.SQLITE_OK)
            {
                //Malloc.sqlite3_free(ref pCur.pKey);
                pCur.pKey = null;
                Debug.Assert(pCur.eState == CursorFlag.CURSOR_VALID || pCur.eState == CursorFlag.CURSOR_INVALID);
            }
            return rc;
        }

        //#define restoreCursorPosition(p) \
        //  (p.eState>=CursorFlag.CURSOR_REQUIRESEEK ? \
        //         btreeRestoreCursorPosition(p) : \
        //         StatusCode.SQLITE_OK)
        private static int restoreCursorPosition(BtCursor pCur)
        {
            if (pCur.eState >= CursorFlag.CURSOR_REQUIRESEEK)
                return btreeRestoreCursorPosition(pCur);
            else
                return StatusCode.SQLITE_OK;
        }

        /*
    ** Determine whether or not a cursor has moved from the position it
    ** was last placed at.  Cursors can move when the row they are pointing
    ** at is deleted out from under them.
    **
    ** This routine returns an error code if something goes wrong.  The
    ** integer pHasMoved is set to one if the cursor has moved and 0 if not.
    */

        private static int sqlite3BtreeCursorHasMoved(BtCursor pCur, ref int pHasMoved)
        {
            int rc;

            rc = restoreCursorPosition(pCur);
            if (rc != 0)
            {
                pHasMoved = 1;
                return rc;
            }
            if (pCur.eState != CursorFlag.CURSOR_VALID || pCur.skipNext != 0)
            {
                pHasMoved = 1;
            }
            else
            {
                pHasMoved = 0;
            }
            return StatusCode.SQLITE_OK;
        }

#if !SQLITE_OMIT_AUTOVACUUM
        /*
** Given a page number of a regular database page, return the page
** number for the pointer-map page that contains the entry for the
** input page number.
*/

        private static uint ptrmapPageno(BtShared pBt, uint pgno)
        {
            int nPagesPerMapPage;
            uint iPtrMap, ret;
            Debug.Assert(MutexHelper.MutexHeld(pBt.mutex));
            nPagesPerMapPage = (pBt.usableSize/5) + 1;
            iPtrMap = (uint) ((pgno - 2)/nPagesPerMapPage);
            ret = (uint) (iPtrMap*nPagesPerMapPage) + 2;
            if (ret == Utility.PENDING_BYTE_PAGE(pBt))
            {
                ret++;
            }
            return ret;
        }

        /*
    ** Write an entry into the pointer map.
    **
    ** This routine updates the pointer map entry for page number 'key'
    ** so that it maps to type 'eType' and parent page number 'pgno'.
    **
    ** If pRC is initially non-zero (non-StatusCode.SQLITE_OK) then this routine is
    ** a no-op.  If an error occurs, the appropriate error code is written
    ** into pRC.
    */

        private static void ptrmapPut(BtShared pBt, uint key, byte eType, uint parent, ref int pRC)
        {
            var pDbPage = new PgHdr(); /* The pointer map page */
            byte[] pPtrmap; /* The pointer map data */
            uint iPtrmap; /* The pointer map page number */
            int offset; /* Offset in pointer map page */
            int rc; /* Return code from subfunctions */

            if (pRC != 0) return;

            Debug.Assert(MutexHelper.MutexHeld(pBt.mutex));
            /* The master-journal page number must never be used as a pointer map page */
            Debug.Assert(false == PTRMAP_ISPAGE(pBt, Utility.PENDING_BYTE_PAGE(pBt)));

            Debug.Assert(pBt.autoVacuum);
            if (key == 0)
            {
                pRC = UnitTest.SQLITE_CORRUPT_BKPT();
                return;
            }
            iPtrmap = PTRMAP_PAGENO(pBt, key);
            rc = sqlite3PagerGet(pBt.pPager, iPtrmap, ref pDbPage);
            if (rc != StatusCode.SQLITE_OK)
            {
                pRC = rc;
                return;
            }
            offset = (int) PTRMAP_PTROFFSET(iPtrmap, key);
            if (offset < 0)
            {
                pRC = UnitTest.SQLITE_CORRUPT_BKPT();
                goto ptrmap_exit;
            }
            pPtrmap = sqlite3PagerGetData(pDbPage);

            if (eType != pPtrmap[offset] || Utility.Sqlite3Get4byte(pPtrmap, offset + 1) != parent)
            {
                TRACE("PTRMAP_UPDATE: %d->(%d,%d)\n", key, eType, parent);
                pRC = rc = PagerHelper.PagerWrite(pDbPage);
                if (rc == StatusCode.SQLITE_OK)
                {
                    pPtrmap[offset] = eType;
                    Utility.Sqlite3Put4byte(pPtrmap, offset + 1, parent);
                }
            }

            ptrmap_exit:
            sqlite3PagerUnref(pDbPage);
        }

        /*
    ** Read an entry from the pointer map.
    **
    ** This routine retrieves the pointer map entry for page 'key', writing
    ** the type and parent page number to pEType and puint respectively.
    ** An error code is returned if something goes wrong, otherwise StatusCode.SQLITE_OK.
    */

        private static int ptrmapGet(BtShared pBt, uint key, ref byte pEType, ref uint puint)
        {
            var pDbPage = new PgHdr(); /* The pointer map page */
            int iPtrmap; /* Pointer map page index */
            byte[] pPtrmap; /* Pointer map page data */
            int offset; /* Offset of entry in pointer map */
            int rc;

            Debug.Assert(MutexHelper.MutexHeld(pBt.mutex));

            iPtrmap = (int) PTRMAP_PAGENO(pBt, key);
            rc = sqlite3PagerGet(pBt.pPager, (uint) iPtrmap, ref pDbPage);
            if (rc != 0)
            {
                return rc;
            }
            pPtrmap = sqlite3PagerGetData(pDbPage);

            offset = (int) PTRMAP_PTROFFSET((uint) iPtrmap, key);
            // Under C# pEType will always exist. No need to test; //
            //Debug.Assert( pEType != 0 );
            pEType = pPtrmap[offset];
            // Under C# puint will always exist. No need to test; //
            //if ( puint != 0 )
            puint = Utility.Sqlite3Get4byte(pPtrmap, offset + 1);

            sqlite3PagerUnref(pDbPage);
            if (pEType < 1 || pEType > 5)
                return UnitTest.SQLITE_CORRUPT_BKPT();
            return StatusCode.SQLITE_OK;
        }

#else //* if defined SQLITE_OMIT_AUTOVACUUM */
//#define ptrmapPut(w,x,y,z,rc)
//#define ptrmapGet(w,x,y,z) StatusCode.SQLITE_OK
//#define ptrmapPutOvflPtr(x, y, rc)
#endif

        /*
** Given a btree page and a cell index (0 means the first cell on
** the page, 1 means the second cell, and so forth) return a pointer
** to the cell content.
**
** This routine works only for pages that do not contain overflow cells.
*/
        //#define findCell(P,I) \
        //  ((P).aData + ((P).maskPage & get2byte((P).aData[(P).cellOffset+2*(I)])))
        private static int findCell(MemPage pPage, int iCell)
        {
            return get2byte(pPage.aData, pPage.cellOffset + 2*(iCell));
        }

        /*
    ** This a more complex version of findCell() that works for
    ** pages that do contain overflow cells.
    */

        private static int findOverflowCell(MemPage pPage, int iCell)
        {
            int i;
            Debug.Assert(MutexHelper.MutexHeld(pPage.pBt.mutex));
            for (i = pPage.nOverflow - 1; i >= 0; i--)
            {
                int k;
                _OvflCell pOvfl;
                pOvfl = pPage.aOvfl[i];
                k = pOvfl.idx;
                if (k <= iCell)
                {
                    if (k == iCell)
                    {
                        //return pOvfl.pCell;
                        return -i - 1; // Negative Offset means overflow cells
                    }
                    iCell--;
                }
            }
            return findCell(pPage, iCell);
        }

        /*
    ** Parse a cell content block and fill in the CellInfo structure.  There
    ** are two versions of this function.  btreeParseCell() takes a
    ** cell index as the second argument and btreeParseCellPtr()
    ** takes a pointer to the body of the cell as its second argument.
    **
    ** Within this file, the parseCell() macro can be called instead of
    ** btreeParseCellPtr(). Using some compilers, this will be faster.
    */
        //OVERLOADS
        private static void btreeParseCellPtr(
            MemPage pPage, /* Page containing the cell */
            int iCell, /* Pointer to the cell text. */
            ref CellInfo pInfo /* Fill in this structure */
            )
        {
            btreeParseCellPtr(pPage, pPage.aData, iCell, ref pInfo);
        }

        private static void btreeParseCellPtr(
            MemPage pPage, /* Page containing the cell */
            byte[] pCell, /* The actual data */
            ref CellInfo pInfo /* Fill in this structure */
            )
        {
            btreeParseCellPtr(pPage, pCell, 0, ref pInfo);
        }

        private static void btreeParseCellPtr(
            MemPage pPage, /* Page containing the cell */
            byte[] pCell, /* Pointer to the cell text. */
            int iCell, /* Pointer to the cell text. */
            ref CellInfo pInfo /* Fill in this structure */
            )
        {
            ushort n; /* Number bytes in cell content header */
            uint nPayload = 0; /* Number of bytes of cell payload */

            Debug.Assert(MutexHelper.MutexHeld(pPage.pBt.mutex));

            if (pInfo.pCell != pCell) pInfo.pCell = pCell;
            pInfo.iCell = iCell;
            Debug.Assert(pPage.leaf == 0 || pPage.leaf == 1);
            n = pPage.childPtrSize;
            Debug.Assert(n == 4 - 4*pPage.leaf);
            if (pPage.intKey != 0)
            {
                if (pPage.hasData != 0)
                {
                    n += (ushort)Utility.GetVarint32(pCell, iCell + n, ref nPayload);
                }
                else
                {
                    nPayload = 0;
                }
                n += (ushort) Utility.GetVarint(pCell, iCell + n, ref pInfo.nKey);
                pInfo.nData = nPayload;
            }
            else
            {
                pInfo.nData = 0;
                n += (ushort) Utility.GetVarint32(pCell, iCell + n, ref nPayload);
                pInfo.nKey = nPayload;
            }
            pInfo.nPayload = nPayload;
            pInfo.nHeader = n;
            UnitTest.TestCase(nPayload == pPage.maxLocal);
            UnitTest.TestCase(nPayload == pPage.maxLocal + 1);
            if (Utility.Likely(nPayload <= pPage.maxLocal))
            {
                /* This is the (easy) common case where the entire payload fits
        ** on the local page.  No overflow is required.
        */
                int nSize; /* Total size of cell content in bytes */
                nSize = (int) nPayload + n;
                pInfo.nLocal = (ushort) nPayload;
                pInfo.iOverflow = 0;
                if ((nSize & ~3) == 0)
                {
                    nSize = 4; /* Minimum cell size is 4 */
                }
                pInfo.nSize = (ushort) nSize;
            }
            else
            {
                /* If the payload will not fit completely on the local page, we have
        ** to decide how much to store locally and how much to spill onto
        ** overflow pages.  The strategy is to minimize the amount of unused
        ** space on overflow pages while keeping the amount of local storage
        ** in between minLocal and maxLocal.
        **
        ** Warning:  changing the way overflow payload is distributed in any
        ** way will result in an incompatible file format.
        */
                int minLocal; /* Minimum amount of payload held locally */
                int maxLocal; /* Maximum amount of payload held locally */
                int surplus; /* Overflow payload available for local storage */

                minLocal = pPage.minLocal;
                maxLocal = pPage.maxLocal;
                surplus = (int) (minLocal + (nPayload - minLocal)%(pPage.pBt.usableSize - 4));
                UnitTest.TestCase(surplus == maxLocal);
                UnitTest.TestCase(surplus == maxLocal + 1);
                if (surplus <= maxLocal)
                {
                    pInfo.nLocal = (ushort) surplus;
                }
                else
                {
                    pInfo.nLocal = (ushort) minLocal;
                }
                pInfo.iOverflow = (ushort) (pInfo.nLocal + n);
                pInfo.nSize = (ushort) (pInfo.iOverflow + 4);
            }
        }

        //#define parseCell(pPage, iCell, pInfo) \
        //  btreeParseCellPtr((pPage), findCell((pPage), (iCell)), (pInfo))
        private static void parseCell(MemPage pPage, int iCell, ref CellInfo pInfo)
        {
            btreeParseCellPtr(pPage, findCell(pPage, iCell), ref pInfo);
        }

        private static void btreeParseCell(
            MemPage pPage, /* Page containing the cell */
            int iCell, /* The cell index.  First cell is 0 */
            ref CellInfo pInfo /* Fill in this structure */
            )
        {
            parseCell(pPage, iCell, ref pInfo);
        }

        /*
    ** Compute the total number of bytes that a Cell needs in the cell
    ** data area of the btree-page.  The return number includes the cell
    ** data header and the local payload, but not any overflow page or
    ** the space used by the cell pointer.
    */
        // Alternative form for C#
        private static ushort cellSizePtr(MemPage pPage, int iCell)
        {
            var info = new CellInfo();
            var pCell = new byte[13];
            //byte[] pCell = new byte[13];// Minimum Size = (2 bytes of Header  or (4) Child Pointer) + (maximum of) 9 bytes data
            if (iCell < 0) // Overflow Cell
                Buffer.BlockCopy(pPage.aOvfl[-(iCell + 1)].pCell, 0, pCell, 0,
                                 pCell.Length < pPage.aOvfl[-(iCell + 1)].pCell.Length
                                     ? pCell.Length
                                     : pPage.aOvfl[-(iCell + 1)].pCell.Length);
            else if (iCell >= pPage.aData.Length + 1 - pCell.Length)
                Buffer.BlockCopy(pPage.aData, iCell, pCell, 0, pPage.aData.Length - iCell);
            else
                Buffer.BlockCopy(pPage.aData, iCell, pCell, 0, pCell.Length);
            btreeParseCellPtr(pPage, pCell, ref info);
            return info.nSize;
        }

        // Alternative form for C#
        private static ushort cellSizePtr(MemPage pPage, byte[] pCell, int offset)
        {
            var info = new CellInfo();
            info.pCell = Malloc.sqlite3Malloc(pCell.Length);
            Buffer.BlockCopy(pCell, offset, info.pCell, 0, pCell.Length - offset);
            btreeParseCellPtr(pPage, info.pCell, ref info);
            return info.nSize;
        }

        private static ushort cellSizePtr(MemPage pPage, byte[] pCell)
        {
            int _pIter = pPage.childPtrSize; //byte pIter = &pCell[pPage.childPtrSize];
            uint nSize = 0;

#if SQLITE_DEBUG || DEBUG
            /* The value returned by this function should always be the same as
** the (CellInfo.nSize) value found by doing a full parse of the
** cell. If SQLITE_DEBUG is defined, an Debug.Assert() at the bottom of
** this function verifies that this invariant is not violated. */
            var debuginfo = new CellInfo();
            btreeParseCellPtr(pPage, pCell, ref debuginfo);
#else
      CellInfo debuginfo = new CellInfo();
#endif

            if (pPage.intKey != 0)
            {
                int pEnd;
                if (pPage.hasData != 0)
                {
                    _pIter += Utility.GetVarint32(pCell, ref nSize); // pIter += Utility.GetVarint32( pIter, ref nSize );
                }
                else
                {
                    nSize = 0;
                }

                /* pIter now points at the 64-bit integer key value, a variable length
        ** integer. The following block moves pIter to point at the first byte
        ** past the end of the key value. */
                pEnd = _pIter + 9; //pEnd = &pIter[9];
                while (((pCell[_pIter++]) & 0x80) != 0 && _pIter < pEnd) ; //while( (pIter++)&0x80 && pIter<pEnd );
            }
            else
            {
                _pIter += Utility.GetVarint32(pCell, _pIter, ref nSize); //pIter += Utility.GetVarint32( pIter, ref nSize );
            }

            UnitTest.TestCase(nSize == pPage.maxLocal);
            UnitTest.TestCase(nSize == pPage.maxLocal + 1);
            if (nSize > pPage.maxLocal)
            {
                int minLocal = pPage.minLocal;
                nSize = (uint) (minLocal + (nSize - minLocal)%(pPage.pBt.usableSize - 4));
                UnitTest.TestCase(nSize == pPage.maxLocal);
                UnitTest.TestCase(nSize == pPage.maxLocal + 1);
                if (nSize > pPage.maxLocal)
                {
                    nSize = (uint) minLocal;
                }
                nSize += 4;
            }
            nSize += (uint) _pIter; //nSize += (uint)(pIter - pCell);

            /* The minimum size of any cell is 4 bytes. */
            if (nSize < 4)
            {
                nSize = 4;
            }

            Debug.Assert(nSize == debuginfo.nSize);
            return (ushort) nSize;
        }

#if SQLITE_DEBUG
        /* This variation on cellSizePtr() is used inside of assert() statements
** only. */

        private static ushort cellSize(MemPage pPage, int iCell)
        {
            return cellSizePtr(pPage, findCell(pPage, iCell));
        }
#else
static int cellSize(MemPage pPage, int iCell) { return -1; }
#endif

#if !SQLITE_OMIT_AUTOVACUUM
        /*
** If the cell pCell, part of page pPage contains a pointer
** to an overflow page, insert an entry into the pointer-map
** for the overflow page.
*/

        private static void ptrmapPutOvflPtr(MemPage pPage, int pCell, ref int pRC)
        {
            if (pRC != 0) return;
            var info = new CellInfo();
            Debug.Assert(pCell != 0);
            btreeParseCellPtr(pPage, pCell, ref info);
            Debug.Assert((info.nData + (pPage.intKey != 0 ? 0 : info.nKey)) == info.nPayload);
            if (info.iOverflow != 0)
            {
                uint ovfl = Utility.Sqlite3Get4byte(pPage.aData, pCell, info.iOverflow);
                ptrmapPut(pPage.pBt, ovfl, PTRMAP_OVERFLOW1, pPage.pgno, ref pRC);
            }
        }

        private static void ptrmapPutOvflPtr(MemPage pPage, byte[] pCell, ref int pRC)
        {
            if (pRC != 0) return;
            var info = new CellInfo();
            Debug.Assert(pCell != null);
            btreeParseCellPtr(pPage, pCell, ref info);
            Debug.Assert((info.nData + (pPage.intKey != 0 ? 0 : info.nKey)) == info.nPayload);
            if (info.iOverflow != 0)
            {
                uint ovfl = Utility.Sqlite3Get4byte(pCell, info.iOverflow);
                ptrmapPut(pPage.pBt, ovfl, PTRMAP_OVERFLOW1, pPage.pgno, ref pRC);
            }
        }
#endif


        /*
** Defragment the page given.  All Cells are moved to the
** end of the page and all free space is collected into one
** big FreeBlk that occurs in between the header and cell
** pointer array and the cell content area.
*/

        private static int defragmentPage(MemPage pPage)
        {
            int i; /* Loop counter */
            int pc; /* Address of a i-th cell */
            int addr; /* Offset of first byte after cell pointer array */
            int hdr; /* Offset to the page header */
            int size; /* Size of a cell */
            int usableSize; /* Number of usable bytes on a page */
            int cellOffset; /* Offset to the cell pointer array */
            int cbrk; /* Offset to the cell content area */
            int nCell; /* Number of cells on the page */
            byte[] data; /* The page data */
            byte[] temp; /* Temp area for cell content */
            int iCellFirst; /* First allowable cell index */
            int iCellLast; /* Last possible cell index */


            Debug.Assert(sqlite3PagerIswriteable(pPage.pDbPage));
            Debug.Assert(pPage.pBt != null);
            Debug.Assert(pPage.pBt.usableSize <= SqliteLimit.SQLITE_MAX_PAGE_SIZE);
            Debug.Assert(pPage.nOverflow == 0);
            Debug.Assert(MutexHelper.MutexHeld(pPage.pBt.mutex));
            temp = sqlite3PagerTempSpace(pPage.pBt.pPager);
            data = pPage.aData;
            hdr = pPage.hdrOffset;
            cellOffset = pPage.cellOffset;
            nCell = pPage.nCell;
            Debug.Assert(nCell == get2byte(data, hdr + 3));
            usableSize = pPage.pBt.usableSize;
            cbrk = get2byte(data, hdr + 5);
            Buffer.BlockCopy(data, cbrk, temp, cbrk, usableSize - cbrk);
                //memcpy( temp[cbrk], ref data[cbrk], usableSize - cbrk );
            cbrk = usableSize;
            iCellFirst = cellOffset + 2*nCell;
            iCellLast = usableSize - 4;
            for (i = 0; i < nCell; i++)
            {
                int pAddr; /* The i-th cell pointer */
                pAddr = cellOffset + i*2; // &data[cellOffset + i * 2];
                pc = get2byte(data, pAddr);
                UnitTest.TestCase(pc == iCellFirst);
                UnitTest.TestCase(pc == iCellLast);
#if !(SQLITE_ENABLE_OVERSIZE_CELL_CHECK)
/* These conditions have already been verified in btreeInitPage()
** if SQLITE_ENABLE_OVERSIZE_CELL_CHECK is defined
*/
                if (pc < iCellFirst || pc > iCellLast)
                {
                    return UnitTest.SQLITE_CORRUPT_BKPT();
                }
#endif
                Debug.Assert(pc >= iCellFirst && pc <= iCellLast);
                size = cellSizePtr(pPage, temp, pc);
                cbrk -= size;
#if (SQLITE_ENABLE_OVERSIZE_CELL_CHECK)
        if ( cbrk < iCellFirst )
        {
          return UnitTest.SQLITE_CORRUPT_BKPT();
        }
#else
                if (cbrk < iCellFirst || pc + size > usableSize)
                {
                    return UnitTest.SQLITE_CORRUPT_BKPT();
                }
#endif
                Debug.Assert(cbrk + size <= usableSize && cbrk >= iCellFirst);
                UnitTest.TestCase(cbrk + size == usableSize);
                UnitTest.TestCase(pc + size == usableSize);
                Buffer.BlockCopy(temp, pc, data, cbrk, size); //memcpy(data[cbrk], ref temp[pc], size);
                put2byte(data, pAddr, cbrk);
            }
            Debug.Assert(cbrk >= iCellFirst);
            put2byte(data, hdr + 5, cbrk);
            data[hdr + 1] = 0;
            data[hdr + 2] = 0;
            data[hdr + 7] = 0;
            addr = cellOffset + 2*nCell;
            Array.Clear(data, addr, cbrk - addr); //memset(data[iCellFirst], 0, cbrk-iCellFirst);
            Debug.Assert(sqlite3PagerIswriteable(pPage.pDbPage));
            if (cbrk - iCellFirst != pPage.nFree)
            {
                return UnitTest.SQLITE_CORRUPT_BKPT();
            }
            return StatusCode.SQLITE_OK;
        }

        /*
    ** Allocate nByte bytes of space from within the B-Tree page passed
    ** as the first argument. Write into pIdx the index into pPage.aData[]
    ** of the first byte of allocated space. Return either StatusCode.SQLITE_OK or
    ** an error code (usually StatusCode.SQLITE_CORRUPT).
    **
    ** The caller guarantees that there is sufficient space to make the
    ** allocation.  This routine might need to defragment in order to bring
    ** all the space together, however.  This routine will avoid using
    ** the first two bytes past the cell pointer area since presumably this
    ** allocation is being made in order to insert a new cell, so we will
    ** also end up needing a new cell pointer.
    */

        private static int allocateSpace(MemPage pPage, int nByte, ref int pIdx)
        {
            int hdr = pPage.hdrOffset; /* Local cache of pPage.hdrOffset */
            byte[] data = pPage.aData; /* Local cache of pPage.aData */
            int nFrag; /* Number of fragmented bytes on pPage */
            int top; /* First byte of cell content area */
            int gap; /* First byte of gap between cell pointers and cell content */
            int rc; /* Integer return code */
            int usableSize; /* Usable size of the page */

            Debug.Assert(sqlite3PagerIswriteable(pPage.pDbPage));
            Debug.Assert(pPage.pBt != null);
            Debug.Assert(MutexHelper.MutexHeld(pPage.pBt.mutex));
            Debug.Assert(nByte >= 0); /* Minimum cell size is 4 */
            Debug.Assert(pPage.nFree >= nByte);
            Debug.Assert(pPage.nOverflow == 0);
            usableSize = pPage.pBt.usableSize;
            Debug.Assert(nByte < usableSize - 8);

            nFrag = data[hdr + 7];
            Debug.Assert(pPage.cellOffset == hdr + 12 - 4*pPage.leaf);
            gap = pPage.cellOffset + 2*pPage.nCell;
            top = get2byte(data, hdr + 5);
            if (gap > top)
                return UnitTest.SQLITE_CORRUPT_BKPT();
            UnitTest.TestCase(gap + 2 == top);
            UnitTest.TestCase(gap + 1 == top);
            UnitTest.TestCase(gap == top);

            if (nFrag >= 60)
            {
                /* Always defragment highly fragmented pages */
                rc = defragmentPage(pPage);
                if (rc != 0) return rc;
                top = get2byte(data, hdr + 5);
            }
            else if (gap + 2 <= top)
            {
                /* Search the freelist looking for a free slot big enough to satisfy
        ** the request. The allocation is made from the first free slot in
        ** the list that is large enough to accomadate it.
        */
                int pc, addr;
                for (addr = hdr + 1; (pc = get2byte(data, addr)) > 0; addr = pc)
                {
                    int size; /* Size of free slot */
                    if (pc > usableSize - 4 || pc < addr + 4)
                    {
                        return UnitTest.SQLITE_CORRUPT_BKPT();
                    }
                    size = get2byte(data, pc + 2);
                    if (size >= nByte)
                    {
                        int x = size - nByte;
                        UnitTest.TestCase(x == 4);
                        UnitTest.TestCase(x == 3);
                        if (x < 4)
                        {
                            /* Remove the slot from the free-list. Update the number of
              ** fragmented bytes within the page. */
                            data[addr + 0] = data[pc + 0];
                            data[addr + 1] = data[pc + 1]; //memcpy( data[addr], ref data[pc], 2 );
                            data[hdr + 7] = (byte) (nFrag + x);
                        }
                        else if (size + pc > usableSize)
                        {
                            return UnitTest.SQLITE_CORRUPT_BKPT();
                        }
                        else
                        {
                            /* The slot remains on the free-list. Reduce its size to account
              ** for the portion used by the new allocation. */
                            put2byte(data, pc + 2, x);
                        }
                        pIdx = pc + x;
                        return StatusCode.SQLITE_OK;
                    }
                }
            }

            /* Check to make sure there is enough space in the gap to satisfy
      ** the allocation.  If not, defragment.
      */
            UnitTest.TestCase(gap + 2 + nByte == top);
            if (gap + 2 + nByte > top)
            {
                rc = defragmentPage(pPage);
                if (rc != 0) return rc;
                top = get2byte(data, hdr + 5);
                Debug.Assert(gap + nByte <= top);
            }


            /* Allocate memory from the gap in between the cell pointer array
      ** and the cell content area.  The btreeInitPage() call has already
      ** validated the freelist.  Given that the freelist is valid, there
      ** is no way that the allocation can extend off the end of the page.
      ** The Debug.Assert() below verifies the previous sentence.
      */
            top -= nByte;
            put2byte(data, hdr + 5, top);
            Debug.Assert(top + nByte <= pPage.pBt.usableSize);
            pIdx = top;
            return StatusCode.SQLITE_OK;
        }

        /*
    ** Return a section of the pPage.aData to the freelist.
    ** The first byte of the new free block is pPage.aDisk[start]
    ** and the size of the block is "size" bytes.
    **
    ** Most of the effort here is involved in coalesing adjacent
    ** free blocks into a single big free block.
    */

        private static int freeSpace(MemPage pPage, int start, int size)
        {
            int addr, pbegin, hdr;
            int iLast; /* Largest possible freeblock offset */
            byte[] data = pPage.aData;

            Debug.Assert(pPage.pBt != null);
            Debug.Assert(sqlite3PagerIswriteable(pPage.pDbPage));
            Debug.Assert(start >= pPage.hdrOffset + 6 + pPage.childPtrSize);
            Debug.Assert((start + size) <= pPage.pBt.usableSize);
            Debug.Assert(MutexHelper.MutexHeld(pPage.pBt.mutex));
            Debug.Assert(size >= 0); /* Minimum cell size is 4 */

            if (pPage.pBt.secureDelete)
            {
                /* Overwrite deleted information with zeros when the secure_delete
        ** option is enabled */
                Array.Clear(data, start, size); // memset(&data[start], 0, size);
            }

            /* Add the space back into the linked list of freeblocks.  Note that
** even though the freeblock list was checked by btreeInitPage(),
** btreeInitPage() did not detect overlapping cells or
** freeblocks that overlapped cells.   Nor does it detect when the
** cell content area exceeds the value in the page header.  If these
** situations arise, then subsequent insert operations might corrupt
** the freelist.  So we do need to check for corruption while scanning
** the freelist.
*/
            hdr = pPage.hdrOffset;
            addr = hdr + 1;
            iLast = pPage.pBt.usableSize - 4;
            Debug.Assert(start <= iLast);
            while ((pbegin = get2byte(data, addr)) < start && pbegin > 0)
            {
                if (pbegin < addr + 4)
                {
                    return UnitTest.SQLITE_CORRUPT_BKPT();
                }
                addr = pbegin;
            }
            if (pbegin > iLast)
            {
                return UnitTest.SQLITE_CORRUPT_BKPT();
            }
            Debug.Assert(pbegin > addr || pbegin == 0);
            put2byte(data, addr, start);
            put2byte(data, start, pbegin);
            put2byte(data, start + 2, size);
            pPage.nFree = (ushort) (pPage.nFree + size);

            /* Coalesce adjacent free blocks */
            addr = hdr + 1;
            while ((pbegin = get2byte(data, addr)) > 0)
            {
                int pnext, psize, x;
                Debug.Assert(pbegin > addr);
                Debug.Assert(pbegin <= pPage.pBt.usableSize - 4);
                pnext = get2byte(data, pbegin);
                psize = get2byte(data, pbegin + 2);
                if (pbegin + psize + 3 >= pnext && pnext > 0)
                {
                    int frag = pnext - (pbegin + psize);
                    if ((frag < 0) || (frag > data[hdr + 7]))
                    {
                        return UnitTest.SQLITE_CORRUPT_BKPT();
                    }
                    data[hdr + 7] -= (byte) frag;
                    x = get2byte(data, pnext);
                    put2byte(data, pbegin, x);
                    x = pnext + get2byte(data, pnext + 2) - pbegin;
                    put2byte(data, pbegin + 2, x);
                }
                else
                {
                    addr = pbegin;
                }
            }

            /* If the cell content area begins with a freeblock, remove it. */
            if (data[hdr + 1] == data[hdr + 5] && data[hdr + 2] == data[hdr + 6])
            {
                int top;
                pbegin = get2byte(data, hdr + 1);
                put2byte(data, hdr + 1, get2byte(data, pbegin)); //memcpy( data[hdr + 1], ref data[pbegin], 2 );
                top = get2byte(data, hdr + 5) + get2byte(data, pbegin + 2);
                put2byte(data, hdr + 5, top);
            }
            Debug.Assert(sqlite3PagerIswriteable(pPage.pDbPage));
            return StatusCode.SQLITE_OK;
        }

        /*
    ** Decode the flags byte (the first byte of the header) for a page
    ** and initialize fields of the MemPage structure accordingly.
    **
    ** Only the following combinations are supported.  Anything different
    ** indicates a corrupt database files:
    **
    **         PageTypeFlag.PTF_ZERODATA
    **         PageTypeFlag.PTF_ZERODATA | PageTypeFlag.PTF_LEAF
    **         PageTypeFlag.PTF_LEAFDATA | PageTypeFlag.PTF_INTKEY
    **         PageTypeFlag.PTF_LEAFDATA | PageTypeFlag.PTF_INTKEY | PageTypeFlag.PTF_LEAF
    */

        private static int decodeFlags(MemPage pPage, int flagByte)
        {
            BtShared pBt; /* A copy of pPage.pBt */

            Debug.Assert(pPage.hdrOffset == (pPage.pgno == 1 ? 100 : 0));
            Debug.Assert(MutexHelper.MutexHeld(pPage.pBt.mutex));
            pPage.leaf = (byte) (flagByte >> 3);
            Debug.Assert(PageTypeFlag.PTF_LEAF == 1 << 3);
            flagByte &= ~PageTypeFlag.PTF_LEAF;
            pPage.childPtrSize = (byte) (4 - 4*pPage.leaf);
            pBt = pPage.pBt;
            if (flagByte == (PageTypeFlag.PTF_LEAFDATA | PageTypeFlag.PTF_INTKEY))
            {
                pPage.intKey = 1;
                pPage.hasData = pPage.leaf;
                pPage.maxLocal = pBt.maxLeaf;
                pPage.minLocal = pBt.minLeaf;
            }
            else if (flagByte == PageTypeFlag.PTF_ZERODATA)
            {
                pPage.intKey = 0;
                pPage.hasData = 0;
                pPage.maxLocal = pBt.maxLocal;
                pPage.minLocal = pBt.minLocal;
            }
            else
            {
                return UnitTest.SQLITE_CORRUPT_BKPT();
            }
            return StatusCode.SQLITE_OK;
        }

        /*
    ** Initialize the auxiliary information for a disk block.
    **
    ** Return StatusCode.SQLITE_OK on success.  If we see that the page does
    ** not contain a well-formed database page, then return
    ** StatusCode.SQLITE_CORRUPT.  Note that a return of StatusCode.SQLITE_OK does not
    ** guarantee that the page is well-formed.  It only shows that
    ** we failed to detect any corruption.
    */

        private static int btreeInitPage(MemPage pPage)
        {
            Debug.Assert(pPage.pBt != null);
            Debug.Assert(MutexHelper.MutexHeld(pPage.pBt.mutex));
            Debug.Assert(pPage.pgno == sqlite3PagerPagenumber(pPage.pDbPage));
            Debug.Assert(pPage == sqlite3PagerGetExtra(pPage.pDbPage));
            Debug.Assert(pPage.aData == sqlite3PagerGetData(pPage.pDbPage));

            if (0 == pPage.isInit)
            {
                ushort pc; /* Address of a freeblock within pPage.aData[] */
                byte hdr; /* Offset to beginning of page header */
                byte[] data; /* Equal to pPage.aData */
                BtShared pBt; /* The main btree structure */
                ushort usableSize; /* Amount of usable space on each page */
                ushort cellOffset; /* Offset from start of page to first cell pointer */
                ushort nFree; /* Number of unused bytes on the page */
                ushort top; /* First byte of the cell content area */
                int iCellFirst; /* First allowable cell or freeblock offset */
                int iCellLast; /* Last possible cell or freeblock offset */

                pBt = pPage.pBt;

                hdr = pPage.hdrOffset;
                data = pPage.aData;
                if (decodeFlags(pPage, data[hdr]) != 0)
                    return UnitTest.SQLITE_CORRUPT_BKPT();
                Debug.Assert(pBt.pageSize >= 512 && pBt.pageSize <= 32768);
                pPage.maskPage = (ushort) (pBt.pageSize - 1);
                pPage.nOverflow = 0;
                usableSize = pBt.usableSize;
                pPage.cellOffset = (cellOffset = (ushort) (hdr + 12 - 4*pPage.leaf));
                top = (ushort) get2byte(data, hdr + 5);
                pPage.nCell = (ushort) (get2byte(data, hdr + 3));
                if (pPage.nCell > MX_CELL(pBt))
                {
                    /* To many cells for a single page.  The page must be corrupt */
                    return UnitTest.SQLITE_CORRUPT_BKPT();
                }
                UnitTest.TestCase(pPage.nCell == MX_CELL(pBt));

                /* A malformed database page might cause us to read past the end
        ** of page when parsing a cell.
        **
        ** The following block of code checks early to see if a cell extends
        ** past the end of a page boundary and causes StatusCode.SQLITE_CORRUPT to be
        ** returned if it does.
        */
                iCellFirst = cellOffset + 2*pPage.nCell;
                iCellLast = usableSize - 4;
#if (SQLITE_ENABLE_OVERSIZE_CELL_CHECK)
        {
          int i;            /* Index into the cell pointer array */
          int sz;           /* Size of a cell */

          if ( 0 == pPage.leaf ) iCellLast--;
          for ( i = 0; i < pPage.nCell; i++ )
          {
            pc = (ushort)get2byte( data, cellOffset + i * 2 );
            UnitTest.TestCase( pc == iCellFirst );
            UnitTest.TestCase( pc == iCellLast );
            if ( pc < iCellFirst || pc > iCellLast )
            {
              return UnitTest.SQLITE_CORRUPT_BKPT();
            }
            sz = cellSizePtr( pPage, data, pc );
            UnitTest.TestCase( pc + sz == usableSize );
            if ( pc + sz > usableSize )
            {
              return UnitTest.SQLITE_CORRUPT_BKPT();
            }
          }
          if ( 0 == pPage.leaf ) iCellLast++;
        }
#endif

                /* Compute the total free space on the page */
                pc = (ushort) get2byte(data, hdr + 1);
                nFree = (ushort) (data[hdr + 7] + top);
                while (pc > 0)
                {
                    ushort next, size;
                    if (pc < iCellFirst || pc > iCellLast)
                    {
                        /* Start of free block is off the page */
                        return UnitTest.SQLITE_CORRUPT_BKPT();
                    }
                    next = (ushort) get2byte(data, pc);
                    size = (ushort) get2byte(data, pc + 2);
                    if ((next > 0 && next <= pc + size + 3) || pc + size > usableSize)
                    {
                        /* Free blocks must be in ascending order. And the last byte of
      ** the free-block must lie on the database page.  */
                        return UnitTest.SQLITE_CORRUPT_BKPT();
                    }
                    nFree = (ushort) (nFree + size);
                    pc = next;
                }

                /* At this point, nFree contains the sum of the offset to the start
        ** of the cell-content area plus the number of free bytes within
        ** the cell-content area. If this is greater than the usable-size
        ** of the page, then the page must be corrupted. This check also
        ** serves to verify that the offset to the start of the cell-content
        ** area, according to the page header, lies within the page.
        */
                if (nFree > usableSize)
                {
                    return UnitTest.SQLITE_CORRUPT_BKPT();
                }
                pPage.nFree = (ushort) (nFree - iCellFirst);
                pPage.isInit = 1;
            }
            return StatusCode.SQLITE_OK;
        }

        /*
    ** Set up a raw page so that it looks like a database page holding
    ** no entries.
    */

        private static void zeroPage(MemPage pPage, int flags)
        {
            byte[] data = pPage.aData;
            BtShared pBt = pPage.pBt;
            byte hdr = pPage.hdrOffset;
            ushort first;

            Debug.Assert(sqlite3PagerPagenumber(pPage.pDbPage) == pPage.pgno);
            Debug.Assert(sqlite3PagerGetExtra(pPage.pDbPage) == pPage);
            Debug.Assert(sqlite3PagerGetData(pPage.pDbPage) == data);
            Debug.Assert(sqlite3PagerIswriteable(pPage.pDbPage));
            Debug.Assert(MutexHelper.MutexHeld(pBt.mutex));
            if (pBt.secureDelete)
            {
                Array.Clear(data, hdr, pBt.usableSize - hdr); //memset(&data[hdr], 0, pBt->usableSize - hdr);
            }

            data[hdr] = (byte) flags;
            first = (ushort) (hdr + 8 + 4*((flags & PageTypeFlag.PTF_LEAF) == 0 ? 1 : 0));
            Array.Clear(data, hdr + 1, 4); //memset(data[hdr+1], 0, 4);
            data[hdr + 7] = 0;
            put2byte(data, hdr + 5, pBt.usableSize);
            pPage.nFree = (ushort) (pBt.usableSize - first);
            decodeFlags(pPage, flags);
            pPage.hdrOffset = hdr;
            pPage.cellOffset = first;
            pPage.nOverflow = 0;
            Debug.Assert(pBt.pageSize >= 512 && pBt.pageSize <= 32768);
            pPage.maskPage = (ushort) (pBt.pageSize - 1);
            pPage.nCell = 0;
            pPage.isInit = 1;
        }


        /*
    ** Convert a DbPage obtained from the pager into a MemPage used by
    ** the btree layer.
    */

        private static MemPage btreePageFromDbPage(DbPage pDbPage, uint pgno, BtShared pBt)
        {
            MemPage pPage = sqlite3PagerGetExtra(pDbPage);
            pPage.aData = sqlite3PagerGetData(pDbPage);
            pPage.pDbPage = pDbPage;
            pPage.pBt = pBt;
            pPage.pgno = pgno;
            pPage.hdrOffset = (byte) (pPage.pgno == 1 ? 100 : 0);
            return pPage;
        }

        /*
    ** Get a page from the pager.  Initialize the MemPage.pBt and
    ** MemPage.aData elements if needed.
    **
    ** If the noContent flag is set, it means that we do not care about
    ** the content of the page at this time.  So do not go to the disk
    ** to fetch the content.  Just fill in the content with zeros for now.
    ** If in the future we call PagerHelper.PagerWrite() on this page, that
    ** means we have started to be concerned about content and the disk
    ** read should occur at that point.
    */

        private static int btreeGetPage(
            BtShared pBt, /* The btree */
            uint pgno, /* Number of the page to fetch */
            ref MemPage ppPage, /* Return the page in this parameter */
            int noContent /* Do not load page content if true */
            )
        {
            int rc;
            DbPage pDbPage = null;

            Debug.Assert(MutexHelper.MutexHeld(pBt.mutex));
            rc = sqlite3PagerAcquire(pBt.pPager, pgno, ref pDbPage, (byte) noContent);
            if (rc != 0) return rc;
            ppPage = btreePageFromDbPage(pDbPage, pgno, pBt);
            return StatusCode.SQLITE_OK;
        }

        /*
    ** Retrieve a page from the pager cache. If the requested page is not
    ** already in the pager cache return NULL. Initialize the MemPage.pBt and
    ** MemPage.aData elements if needed.
    */

        private static MemPage btreePageLookup(BtShared pBt, uint pgno)
        {
            DbPage pDbPage;
            Debug.Assert(MutexHelper.MutexHeld(pBt.mutex));
            pDbPage = sqlite3PagerLookup(pBt.pPager, pgno);
            if (pDbPage)
            {
                return btreePageFromDbPage(pDbPage, pgno, pBt);
            }
            return null;
        }

        /*
    ** Return the size of the database file in pages. If there is any kind of
    ** error, return ((unsigned int)-1).
    */

        private static uint pagerPagecount(BtShared pBt)
        {
            int nPage = -1;
            int rc;
            Debug.Assert(pBt.pPage1 != null);
            rc = sqlite3PagerPagecount(pBt.pPager, ref nPage);
            Debug.Assert(rc == StatusCode.SQLITE_OK || nPage == -1);
            return (uint) nPage;
        }

        /*
    ** Get a page from the pager and initialize it.  This routine is just a
    ** convenience wrapper around separate calls to btreeGetPage() and
    ** btreeInitPage().
    **
    ** If an error occurs, then the value ppPage is set to is undefined. It
    ** may remain unchanged, or it may be set to an invalid value.
    */

        private static int getAndInitPage(
            BtShared pBt, /* The database file */
            uint pgno, /* Number of the page to get */
            ref MemPage ppPage /* Write the page pointer here */
            )
        {
            int rc;
#if !NDEBUG || SQLITE_COVERAGE_TEST
      uint iLastPg = pagerPagecount( pBt );//  TESTONLY( uint iLastPg = pagerPagecount(pBt); )
#else
            const uint iLastPg = uint.MaxValue;
#endif
            Debug.Assert(MutexHelper.MutexHeld(pBt.mutex));

            rc = btreeGetPage(pBt, pgno, ref ppPage, 0);
            if (rc == StatusCode.SQLITE_OK)
            {
                rc = btreeInitPage(ppPage);
                if (rc != StatusCode.SQLITE_OK)
                {
                    releasePage(ppPage);
                }
            }

            /* If the requested page number was either 0 or greater than the page
      ** number of the last page in the database, this function should return
      ** StatusCode.SQLITE_CORRUPT or some other error (i.e. StatusCode.SQLITE_FULL). Check that this
      ** is the case.  */
            Debug.Assert((pgno > 0 && pgno <= iLastPg) || rc != StatusCode.SQLITE_OK);
            UnitTest.TestCase(pgno == 0);
            UnitTest.TestCase(pgno == iLastPg);

            return rc;
        }

        /*
    ** Release a MemPage.  This should be called once for each prior
    ** call to btreeGetPage.
    */

        private static void releasePage(MemPage pPage)
        {
            if (pPage != null)
            {
                Debug.Assert(pPage.aData != null);
                Debug.Assert(pPage.pBt != null);
                Debug.Assert(sqlite3PagerGetExtra(pPage.pDbPage) == pPage);
                Debug.Assert(sqlite3PagerGetData(pPage.pDbPage) == pPage.aData);
                Debug.Assert(MutexHelper.MutexHeld(pPage.pBt.mutex));
                sqlite3PagerUnref(pPage.pDbPage);
            }
        }

        /*
    ** During a rollback, when the pager reloads information into the cache
    ** so that the cache is restored to its original state at the start of
    ** the transaction, for each page restored this routine is called.
    **
    ** This routine needs to reset the extra data section at the end of the
    ** page to agree with the restored data.
    */

        private static void pageReinit(DbPage pData)
        {
            MemPage pPage;
            pPage = sqlite3PagerGetExtra(pData);
            Debug.Assert(sqlite3PagerPageRefcount(pData) > 0);
            if (pPage.isInit != 0)
            {
                Debug.Assert(MutexHelper.MutexHeld(pPage.pBt.mutex));
                pPage.isInit = 0;
                if (sqlite3PagerPageRefcount(pData) > 1)
                {
                    /* pPage might not be a btree page;  it might be an overflow page
          ** or ptrmap page or a free page.  In those cases, the following
          ** call to btreeInitPage() will Utility.Likely return StatusCode.SQLITE_CORRUPT.
          ** But no harm is done by this.  And it is very important that
          ** btreeInitPage() be called on every btree page so we make
          ** the call for every page that comes in for re-initing. */
                    btreeInitPage(pPage);
                }
            }
        }

        /*
    ** Invoke the busy handler for a btree.
    */

        private static int btreeInvokeBusyHandler(object pArg)
        {
            var pBt = (BtShared) pArg;
            Debug.Assert(pBt.db != null);
            Debug.Assert(MutexHelper.MutexHeld(pBt.db.mutex));
            return sqlite3InvokeBusyHandler(pBt.db.busyHandler);
        }

        /*
    ** Open a database file.
    **
    ** zFilename is the name of the database file.  If zFilename is NULL
    ** a new database with a random name is created.  This randomly named
    ** database file will be deleted when sqlite3BtreeClose() is called.
    ** If zFilename is ":memory:" then an in-memory database is created
    ** that is automatically destroyed when it is closed.
    **
    ** If the database is already opened in the same database connection
    ** and we are in shared cache mode, then the open will fail with an
    ** StatusCode.SQLITE_CONSTRAINT error.  We cannot allow two or more BtShared
    ** objects in the same database connection since doing so will lead
    ** to problems with locking.
    */

        private static int sqlite3BtreeOpen(
            string zFilename, /* Name of the file containing the BTree database */
            sqlite3 db, /* Associated database handle */
            ref Btree ppBtree, /* Pointer to new Btree object written here */
            int flags, /* Options */
            int vfsFlags /* Flags passed through to sqlite3_vfs.xOpen() */
            )
        {
            sqlite3_vfs pVfs; /* The VFS to use for this btree */
            BtShared pBt = null; /* Shared part of btree structure */
            Btree p; /* Handle to return */
            sqlite3_mutex mutexOpen = null; /* Prevents a race condition. Ticket #3537 */
            int rc = StatusCode.SQLITE_OK; /* Result code from this function */
            byte nReserve; /* Byte of unused space on each page */
            var zDbHeader = new byte[100]; /* Database header content */

            /* Set the variable isMemdb to true for an in-memory database, or
      ** false for a file-based database. This symbol is only required if
      ** either of the shared-data or autovacuum features are compiled
      ** into the library.
      */
#if !(SQLITE_OMIT_SHARED_CACHE) || !(SQLITE_OMIT_AUTOVACUUM)
#if SQLITE_OMIT_MEMORYDB
bool isMemdb = false;
#else
            bool isMemdb = zFilename == ":memory:";
#endif
#endif

            Debug.Assert(db != null);
            Debug.Assert(MutexHelper.MutexHeld(db.mutex));

            pVfs = db.pVfs;
            p = new Btree(); //Malloc.MallocZero(sizeof(Btree));
            //if( !p ){
            //  return StatusCode.SQLITE_NOMEM;
            //}
            p.inTrans = TransactionType.TRANS_NONE;
            p.db = db;
#if !SQLITE_OMIT_SHARED_CACHE
p.lock.pBtree = p;
p.lock.iTable = 1;
#endif

#if !(SQLITE_OMIT_SHARED_CACHE) && !(SQLITE_OMIT_DISKIO)
/*
** If this Btree is a candidate for shared cache, try to find an
** existing BtShared object that we can share with
*/
if( isMemdb==null && zFilename && zFilename[0] ){
if( vfsFlags & FileOpenOperation.SQLITE_OPEN_SHAREDCACHE ){
int nFullPathname = pVfs.mxPathname+1;
string zFullPathname = Malloc.sqlite3Malloc(nFullPathname);
sqlite3_mutex *mutexShared;
p.sharable = 1;
if( !zFullPathname ){
p = null;//Malloc.sqlite3_free(ref p);
return StatusCode.SQLITE_NOMEM;
}
sqlite3OsFullPathname(pVfs, zFilename, nFullPathname, zFullPathname);
mutexOpen = MutexHelper.MutexAlloc(MutexType.SQLITE_MUTEX_STATIC_OPEN);
MutexHelper.MutexEnter(mutexOpen);
mutexShared = MutexHelper.MutexAlloc(MutexType.SQLITE_MUTEX_STATIC_MASTER);
MutexHelper.MutexEnter(mutexShared);
for(pBt=GLOBAL(BtShared*,sqlite3SharedCacheList); pBt; pBt=pBt.pNext){
Debug.Assert( pBt.nRef>0 );
if( 0==strcmp(zFullPathname, sqlite3PagerFilename(pBt.pPager))
&& sqlite3PagerVfs(pBt.pPager)==pVfs ){
int iDb;
for(iDb=db.nDb-1; iDb>=0; iDb--){
Btree pExisting = db.aDb[iDb].pBt;
if( pExisting && pExisting.pBt==pBt ){
MutexHelper.MutexLeave(mutexShared);
MutexHelper.MutexLeave(mutexOpen);
zFullPathname = null;//Malloc.sqlite3_free(ref zFullPathname);
p=null;//Malloc.sqlite3_free(ref p);
return StatusCode.SQLITE_CONSTRAINT;
}
}
p.pBt = pBt;
pBt.nRef++;
break;
}
}
MutexHelper.MutexLeave(mutexShared);
zFullPathname=null;//Malloc.sqlite3_free(ref zFullPathname);
}
#if SQLITE_DEBUG
else{
/* In debug mode, we mark all persistent databases as sharable
** even when they are not.  This exercises the locking code and
** gives more opportunity for asserts(MutexHelper.MutexHeld())
** statements to find locking problems.
*/
p.sharable = 1;
}
#endif
}
#endif
            if (pBt == null)
            {
                /*
        ** The following asserts make sure that structures used by the btree are
        ** the right size.  This is to guard against size changes that result
        ** when compiling on a different architecture.
        */
                Debug.Assert(sizeof (long) == 8 || sizeof (long) == 4);
                Debug.Assert(sizeof (ulong) == 8 || sizeof (ulong) == 4);
                Debug.Assert(sizeof (uint) == 4);
                Debug.Assert(sizeof (ushort) == 2);
                Debug.Assert(sizeof (uint) == 4);

                pBt = new BtShared(); //Malloc.MallocZero( sizeof(pBt) );
                //if( pBt==null ){
                //  rc = StatusCode.SQLITE_NOMEM;
                //  goto btree_open_out;
                //}
                rc = sqlite3PagerOpen(pVfs, ref pBt.pPager, zFilename,
                                      EXTRA_SIZE, flags, vfsFlags, pageReinit);
                if (rc == StatusCode.SQLITE_OK)
                {
                    rc = sqlite3PagerReadFileheader(pBt.pPager, zDbHeader.Length, zDbHeader);
                }
                if (rc != StatusCode.SQLITE_OK)
                {
                    goto btree_open_out;
                }
                pBt.db = db;
                sqlite3PagerSetBusyhandler(pBt.pPager, btreeInvokeBusyHandler, pBt);
                p.pBt = pBt;

                pBt.pCursor = null;
                pBt.pPage1 = null;
                pBt.readOnly = sqlite3PagerIsreadonly(pBt.pPager);
#if SQLITE_SECURE_DELETE
            pBt.secureDelete = true;
#endif
                pBt.pageSize = (ushort) get2byte(zDbHeader, 16);
                if (pBt.pageSize < 512 || pBt.pageSize > SqliteLimit.SQLITE_MAX_PAGE_SIZE
                    || ((pBt.pageSize - 1) & pBt.pageSize) != 0)
                {
                    pBt.pageSize = 0;
#if !SQLITE_OMIT_AUTOVACUUM
                    /* If the magic name ":memory:" will create an in-memory database, then
** leave the autoVacuum mode at 0 (do not auto-vacuum), even if
** Const.SQLITE_DEFAULT_AUTOVACUUM is true. On the other hand, if
** SQLITE_OMIT_MEMORYDB has been defined, then ":memory:" is just a
** regular file-name. In this case the auto-vacuum applies as per normal.
*/
                    if (zFilename != "" && !isMemdb)
                    {
                        pBt.autoVacuum = (Const.SQLITE_DEFAULT_AUTOVACUUM != 0);
                        pBt.incrVacuum = (Const.SQLITE_DEFAULT_AUTOVACUUM == 2);
                    }
#endif
                    nReserve = 0;
                }
                else
                {
                    nReserve = zDbHeader[20];
                    pBt.pageSizeFixed = true;
#if !SQLITE_OMIT_AUTOVACUUM
                    pBt.autoVacuum = Utility.Sqlite3Get4byte(zDbHeader, 36 + 4*4) != 0;
                    pBt.incrVacuum = Utility.Sqlite3Get4byte(zDbHeader, 36 + 7*4) != 0;
#endif
                }
                rc = sqlite3PagerSetPagesize(pBt.pPager, ref pBt.pageSize, nReserve);
                if (rc != 0) goto btree_open_out;
                pBt.usableSize = (ushort) (pBt.pageSize - nReserve);
                Debug.Assert((pBt.pageSize & 7) == 0); /* 8-byte alignment of pageSize */

#if !(SQLITE_OMIT_SHARED_CACHE) && !(SQLITE_OMIT_DISKIO)
/* Add the new BtShared object to the linked list sharable BtShareds.
*/
if( p.sharable ){
sqlite3_mutex *mutexShared;
pBt.nRef = 1;
mutexShared = MutexHelper.MutexAlloc(MutexType.SQLITE_MUTEX_STATIC_MASTER);
if( SQLITE_THREADSAFE && Global.Config.bCoreMutex ){
pBt.mutex = MutexHelper.MutexAlloc(MutexType.SQLITE_MUTEX_FAST);
if( pBt.mutex==null ){
rc = StatusCode.SQLITE_NOMEM;
db.mallocFailed = 0;
goto btree_open_out;
}
}
MutexHelper.MutexEnter(mutexShared);
pBt.pNext = GLOBAL(BtShared*,sqlite3SharedCacheList);
GLOBAL(BtShared*,sqlite3SharedCacheList) = pBt;
MutexHelper.MutexLeave(mutexShared);
}
#endif
            }

#if !(SQLITE_OMIT_SHARED_CACHE) && !(SQLITE_OMIT_DISKIO)
/* If the new Btree uses a sharable pBtShared, then link the new
** Btree into the list of all sharable Btrees for the same connection.
** The list is kept in ascending order by pBt address.
*/
if( p.sharable ){
int i;
Btree pSib;
for(i=0; i<db.nDb; i++){
if( (pSib = db.aDb[i].pBt)!=null && pSib.sharable ){
while( pSib.pPrev ){ pSib = pSib.pPrev; }
if( p.pBt<pSib.pBt ){
p.pNext = pSib;
p.pPrev = 0;
pSib.pPrev = p;
}else{
while( pSib.pNext && pSib.pNext.pBt<p.pBt ){
pSib = pSib.pNext;
}
p.pNext = pSib.pNext;
p.pPrev = pSib;
if( p.pNext ){
p.pNext.pPrev = p;
}
pSib.pNext = p;
}
break;
}
}
}
#endif
            ppBtree = p;

            btree_open_out:
            if (rc != StatusCode.SQLITE_OK)
            {
                if (pBt != null && pBt.pPager != null)
                {
                    sqlite3PagerClose(pBt.pPager);
                }
                pBt = null; //    Malloc.sqlite3_free(ref pBt);
                p = null; //    Malloc.sqlite3_free(ref p);
                ppBtree = null;
            }
            if (mutexOpen != null)
            {
                Debug.Assert(MutexHelper.MutexHeld(mutexOpen));
                MutexHelper.MutexLeave(mutexOpen);
            }
            return rc;
        }

        /*
    ** Decrement the BtShared.nRef counter.  When it reaches zero,
    ** remove the BtShared structure from the sharing list.  Return
    ** true if the BtShared.nRef counter reaches zero and return
    ** false if it is still positive.
    */

        private static bool removeFromSharingList(BtShared pBt)
        {
#if !SQLITE_OMIT_SHARED_CACHE
sqlite3_mutex pMaster;
BtShared pList;
bool removed = false;

Debug.Assert( MutexHelper.MutexNotheld(pBt.mutex) );
pMaster = MutexHelper.MutexAlloc(MutexType.SQLITE_MUTEX_STATIC_MASTER);
MutexHelper.MutexEnter(pMaster);
pBt.nRef--;
if( pBt.nRef<=0 ){
if( GLOBAL(BtShared*,sqlite3SharedCacheList)==pBt ){
GLOBAL(BtShared*,sqlite3SharedCacheList) = pBt.pNext;
}else{
pList = GLOBAL(BtShared*,sqlite3SharedCacheList);
while( UnitTest.ALWAYS(pList) && pList.pNext!=pBt ){
pList=pList.pNext;
}
if( UnitTest.ALWAYS(pList) ){
pList.pNext = pBt.pNext;
}
}
if( SQLITE_THREADSAFE ){
MutexHelper.MutexFree(pBt.mutex);
}
removed = true;
}
MutexHelper.MutexLeave(pMaster);
return removed;
#else
            return true;
#endif
        }

        /*
    ** Make sure pBt.pTmpSpace points to an allocation of
    ** MX_CELL_SIZE(pBt) bytes.
    */

        private static void allocateTempSpace(BtShared pBt)
        {
            if (null == pBt.pTmpSpace)
            {
                pBt.pTmpSpace = Malloc.sqlite3Malloc(pBt.pageSize);
            }
        }

        /*
    ** Free the pBt.pTmpSpace allocation
    */

        private static void freeTempSpace(BtShared pBt)
        {
            sqlite3PageFree(ref pBt.pTmpSpace);
        }

        /*
** Close an open database and invalidate all cursors.
*/

        private static int sqlite3BtreeClose(ref Btree p)
        {
            BtShared pBt = p.pBt;
            BtCursor pCur;

            /* Close all cursors opened via this handle.  */
            Debug.Assert(MutexHelper.MutexHeld(p.db.mutex));
            sqlite3BtreeEnter(p);
            pCur = pBt.pCursor;
            while (pCur != null)
            {
                BtCursor pTmp = pCur;
                pCur = pCur.pNext;
                if (pTmp.pBtree == p)
                {
                    sqlite3BtreeCloseCursor(pTmp);
                }
                MemPool.MemFreeBtCursor(ref pBt.pCursor);
            }

            /* Rollback any active transaction and free the handle structure.
      ** The call to sqlite3BtreeRollback() drops any table-locks held by
      ** this handle.
      */
            sqlite3BtreeRollback(p);
            sqlite3BtreeLeave(p);

            /* If there are still other outstanding references to the shared-btree
      ** structure, return now. The remainder of this procedure cleans
      ** up the shared-btree.
      */
            Debug.Assert(p.wantToLock == 0 && !p.locked);
            if (!p.sharable || removeFromSharingList(pBt))
            {
                /* The pBt is no longer on the sharing list, so we can access
        ** it without having to hold the mutex.
        **
        ** Clean out and delete the BtShared object.
        */
                Debug.Assert(null == pBt.pCursor);
                sqlite3PagerClose(pBt.pPager);
                if (pBt.xFreeSchema != null && pBt.pSchema != null)
                {
                    pBt.xFreeSchema(pBt.pSchema);
                }
                pBt.pSchema = null; // Malloc.sqlite3_free( ref pBt.pSchema );
                //freeTempSpace(pBt);
                pBt = null; //Malloc.sqlite3_free(ref pBt);
            }

#if !SQLITE_OMIT_SHARED_CACHE
Debug.Assert( p.wantToLock==null );
Debug.Assert( p.locked==null );
if( p.pPrev ) p.pPrev.pNext = p.pNext;
if( p.pNext ) p.pNext.pPrev = p.pPrev;
#endif

            //Malloc.sqlite3_free(ref p);
            return StatusCode.SQLITE_OK;
        }

        /*
    ** Change the limit on the number of pages allowed in the cache.
    **
    ** The maximum number of cache pages is set to the absolute
    ** value of mxPage.  If mxPage is negative, the pager will
    ** operate asynchronously - it will not stop to do fsync()s
    ** to insure data is written to the disk surface before
    ** continuing.  Transactions still work if synchronous is off,
    ** and the database cannot be corrupted if this program
    ** crashes.  But if the operating system crashes or there is
    ** an abrupt power failure when synchronous is off, the database
    ** could be left in an inconsistent and unrecoverable state.
    ** Synchronous is on by default so database corruption is not
    ** normally a worry.
    */

        private static int sqlite3BtreeSetCacheSize(Btree p, int mxPage)
        {
            BtShared pBt = p.pBt;
            Debug.Assert(MutexHelper.MutexHeld(p.db.mutex));
            sqlite3BtreeEnter(p);
            sqlite3PagerSetCachesize(pBt.pPager, mxPage);
            sqlite3BtreeLeave(p);
            return StatusCode.SQLITE_OK;
        }

        /*
    ** Change the way data is synced to disk in order to increase or decrease
    ** how well the database resists damage due to OS crashes and power
    ** failures.  Level 1 is the same as asynchronous (no syncs() occur and
    ** there is a high probability of damage)  Level 2 is the default.  There
    ** is a very low but non-zero probability of damage.  Level 3 reduces the
    ** probability of damage to near zero but with a write performance reduction.
    */
#if !SQLITE_OMIT_PAGER_PRAGMAS
        private static int sqlite3BtreeSetSafetyLevel(Btree p, int level, int fullSync)
        {
            BtShared pBt = p.pBt;
            Debug.Assert(MutexHelper.MutexHeld(p.db.mutex));
            sqlite3BtreeEnter(p);
            sqlite3PagerSetSafetyLevel(pBt.pPager, level, fullSync != 0);
            sqlite3BtreeLeave(p);
            return StatusCode.SQLITE_OK;
        }
#endif

        /*
** Return TRUE if the given btree is set to safety level 1.  In other
** words, return TRUE if no sync() occurs on the disk files.
*/

        private static int sqlite3BtreeSyncDisabled(Btree p)
        {
            BtShared pBt = p.pBt;
            int rc;
            Debug.Assert(MutexHelper.MutexHeld(p.db.mutex));
            sqlite3BtreeEnter(p);
            Debug.Assert(pBt != null && pBt.pPager != null);
            rc = sqlite3PagerNosync(pBt.pPager) ? 1 : 0;
            sqlite3BtreeLeave(p);
            return rc;
        }

#if !(SQLITE_OMIT_PAGER_PRAGMAS) || !(SQLITE_OMIT_VACUUM)
        /*
** Change the default pages size and the number of reserved bytes per page.
** Or, if the page size has already been fixed, return StatusCode.SQLITE_READONLY
** without changing anything.
**
** The page size must be a power of 2 between 512 and 65536.  If the page
** size supplied does not meet this constraint then the page size is not
** changed.
**
** Page sizes are constrained to be a power of two so that the region
** of the database file used for locking (beginning at Global.PENDING_BYTE,
** the first byte past the 1GB boundary, 0x40000000) needs to occur
** at the beginning of a page.
**
** If parameter nReserve is less than zero, then the number of reserved
** bytes per page is left unchanged.
**
** If the iFix!=null then the pageSizeFixed flag is set so that the page size
** and autovacuum mode can no longer be changed.
*/

        private static int sqlite3BtreeSetPageSize(Btree p, int pageSize, int nReserve, int iFix)
        {
            int rc = StatusCode.SQLITE_OK;
            BtShared pBt = p.pBt;
            Debug.Assert(nReserve >= -1 && nReserve <= 255);
            sqlite3BtreeEnter(p);
            if (pBt.pageSizeFixed)
            {
                sqlite3BtreeLeave(p);
                return StatusCode.SQLITE_READONLY;
            }
            if (nReserve < 0)
            {
                nReserve = pBt.pageSize - pBt.usableSize;
            }
            Debug.Assert(nReserve >= 0 && nReserve <= 255);
            if (pageSize >= 512 && pageSize <= SqliteLimit.SQLITE_MAX_PAGE_SIZE &&
                ((pageSize - 1) & pageSize) == 0)
            {
                Debug.Assert((pageSize & 7) == 0);
                Debug.Assert(null == pBt.pPage1 && null == pBt.pCursor);
                pBt.pageSize = (ushort) pageSize;
                //        freeTempSpace(pBt);
            }
            rc = sqlite3PagerSetPagesize(pBt.pPager, ref pBt.pageSize, nReserve);
            pBt.usableSize = (ushort) (pBt.pageSize - nReserve);
            if (iFix != 0) pBt.pageSizeFixed = true;
            sqlite3BtreeLeave(p);
            return rc;
        }

        /*
    ** Return the currently defined page size
    */

        private static int sqlite3BtreeGetPageSize(Btree p)
        {
            return p.pBt.pageSize;
        }

        /*
    ** Return the number of bytes of space at the end of every page that
    ** are intentually left unused.  This is the "reserved" space that is
    ** sometimes used by extensions.
    */

        private static int sqlite3BtreeGetReserve(Btree p)
        {
            int n;
            sqlite3BtreeEnter(p);
            n = p.pBt.pageSize - p.pBt.usableSize;
            sqlite3BtreeLeave(p);
            return n;
        }

        /*
    ** Set the maximum page count for a database if mxPage is positive.
    ** No changes are made if mxPage is 0 or negative.
    ** Regardless of the value of mxPage, return the maximum page count.
    */

        private static int sqlite3BtreeMaxPageCount(Btree p, int mxPage)
        {
            int n;
            sqlite3BtreeEnter(p);
            n = (int) sqlite3PagerMaxPageCount(p.pBt.pPager, mxPage);
            sqlite3BtreeLeave(p);
            return n;
        }

        /*
    ** Set the secureDelete flag if newFlag is 0 or 1.  If newFlag is -1,
    ** then make no changes.  Always return the value of the secureDelete
    ** setting after the change.
    */

        private static int sqlite3BtreeSecureDelete(Btree p, int newFlag)
        {
            int b;
            if (p == null) return 0;
            sqlite3BtreeEnter(p);
            if (newFlag >= 0)
            {
                p.pBt.secureDelete = (newFlag != 0);
            }
            b = p.pBt.secureDelete ? 1 : 0;
            sqlite3BtreeLeave(p);
            return b;
        }
#endif
        //* !(SQLITE_OMIT_PAGER_PRAGMAS) || !(SQLITE_OMIT_VACUUM) */

        /*
** Change the 'auto-vacuum' property of the database. If the 'autoVacuum'
** parameter is non-zero, then auto-vacuum mode is enabled. If zero, it
** is disabled. The default value for the auto-vacuum property is
** determined by the Const.SQLITE_DEFAULT_AUTOVACUUM macro.
*/

        private static int sqlite3BtreeSetAutoVacuum(Btree p, int autoVacuum)
        {
#if SQLITE_OMIT_AUTOVACUUM
return StatusCode.SQLITE_READONLY;
#else
            BtShared pBt = p.pBt;
            int rc = StatusCode.SQLITE_OK;
            var av = (byte) autoVacuum;

            sqlite3BtreeEnter(p);
            if (pBt.pageSizeFixed && (av != 0) != pBt.autoVacuum)
            {
                rc = StatusCode.SQLITE_READONLY;
            }
            else
            {
                pBt.autoVacuum = av != 0;
                pBt.incrVacuum = av == 2;
            }
            sqlite3BtreeLeave(p);
            return rc;
#endif
        }

        /*
    ** Return the value of the 'auto-vacuum' property. If auto-vacuum is
    ** enabled 1 is returned. Otherwise 0.
    */

        private static int sqlite3BtreeGetAutoVacuum(Btree p)
        {
#if SQLITE_OMIT_AUTOVACUUM
return AutoVacuumModel.BTREE_AUTOVACUUM_NONE;
#else
            int rc;
            sqlite3BtreeEnter(p);
            rc = (
                     (!p.pBt.autoVacuum)
                         ? AutoVacuumModel.BTREE_AUTOVACUUM_NONE
                         : (!p.pBt.incrVacuum)
                               ? AutoVacuumModel.BTREE_AUTOVACUUM_FULL
                               : AutoVacuumModel.BTREE_AUTOVACUUM_INCR
                 );
            sqlite3BtreeLeave(p);
            return rc;
#endif
        }


        /*
    ** Get a reference to pPage1 of the database file.  This will
    ** also acquire a readlock on that file.
    **
    ** StatusCode.SQLITE_OK is returned on success.  If the file is not a
    ** well-formed database file, then StatusCode.SQLITE_CORRUPT is returned.
    ** StatusCode.SQLITE_BUSY is returned if the database is locked.  StatusCode.SQLITE_NOMEM
    ** is returned if we run out of memory.
    */

        private static int lockBtree(BtShared pBt)
        {
            int rc;
            var pPage1 = new MemPage();
            int nPage = 0;

            Debug.Assert(MutexHelper.MutexHeld(pBt.mutex));
            Debug.Assert(pBt.pPage1 == null);
            rc = sqlite3PagerSharedLock(pBt.pPager);
            if (rc != StatusCode.SQLITE_OK) return rc;
            rc = btreeGetPage(pBt, 1, ref pPage1, 0);
            if (rc != StatusCode.SQLITE_OK) return rc;

            /* Do some checking to help insure the file we opened really is
      ** a valid database file.
      */
            rc = sqlite3PagerPagecount(pBt.pPager, ref nPage);
            if (rc != StatusCode.SQLITE_OK)
            {
                goto page1_init_failed;
            }
            else if (nPage > 0)
            {
                int pageSize;
                int usableSize;
                byte[] page1 = pPage1.aData;
                rc = StatusCode.SQLITE_NOTADB;
                if (Custom.Memcmp(page1, zMagicHeader, 16) != 0)
                {
                    goto page1_init_failed;
                }
                if (page1[18] > 1)
                {
                    pBt.readOnly = true;
                }
                if (page1[19] > 1)
                {
                    goto page1_init_failed;
                }

                /* The maximum embedded fraction must be exactly 25%.  And the minimum
        ** embedded fraction must be 12.5% for both leaf-data and non-leaf-data.
        ** The original design allowed these amounts to vary, but as of
        ** version 3.6.0, we require them to be fixed.
        */
                if (Custom.Memcmp(page1, 21, "\x0040\x0020\x0020", 3) != 0) //   "\100\040\040"
                {
                    goto page1_init_failed;
                }
                pageSize = get2byte(page1, 16);
                if (((pageSize - 1) & pageSize) != 0 || pageSize < 512 ||
                    (SqliteLimit.SQLITE_MAX_PAGE_SIZE < 32768 && pageSize > SqliteLimit.SQLITE_MAX_PAGE_SIZE)
                    )
                {
                    goto page1_init_failed;
                }
                Debug.Assert((pageSize & 7) == 0);
                usableSize = pageSize - page1[20];
                if (pageSize != pBt.pageSize)
                {
                    /* After reading the first page of the database assuming a page size
          ** of BtShared.pageSize, we have discovered that the page-size is
          ** actually pageSize. Unlock the database, leave pBt.pPage1 at
          ** zero and return StatusCode.SQLITE_OK. The caller will call this function
          ** again with the correct page-size.
          */
                    releasePage(pPage1);
                    pBt.usableSize = (ushort) usableSize;
                    pBt.pageSize = (ushort) pageSize;
                    //          freeTempSpace(pBt);
                    rc = sqlite3PagerSetPagesize(pBt.pPager, ref pBt.pageSize,
                                                 pageSize - usableSize);
                    return rc;
                }
                if (usableSize < 480)
                {
                    goto page1_init_failed;
                }
                pBt.pageSize = (ushort) pageSize;
                pBt.usableSize = (ushort) usableSize;
#if !SQLITE_OMIT_AUTOVACUUM
                pBt.autoVacuum = (Utility.Sqlite3Get4byte(page1, 36 + 4*4) != 0);
                pBt.incrVacuum = (Utility.Sqlite3Get4byte(page1, 36 + 7*4) != 0);
#endif
            }

            /* maxLocal is the maximum amount of payload to store locally for
      ** a cell.  Make sure it is small enough so that at least minFanout
      ** cells can will fit on one page.  We assume a 10-byte page header.
      ** Besides the payload, the cell must store:
      **     2-byte pointer to the cell
      **     4-byte child pointer
      **     9-byte nKey value
      **     4-byte nData value
      **     4-byte overflow page pointer
      ** So a cell consists of a 2-byte poiner, a header which is as much as
      ** 17 bytes long, 0 to N bytes of payload, and an optional 4 byte overflow
      ** page pointer.
      */
            pBt.maxLocal = (ushort) ((pBt.usableSize - 12)*64/255 - 23);
            pBt.minLocal = (ushort) ((pBt.usableSize - 12)*32/255 - 23);
            pBt.maxLeaf = (ushort) (pBt.usableSize - 35);
            pBt.minLeaf = (ushort) ((pBt.usableSize - 12)*32/255 - 23);
            Debug.Assert(pBt.maxLeaf + 23 <= MX_CELL_SIZE(pBt));
            pBt.pPage1 = pPage1;
            return StatusCode.SQLITE_OK;

            page1_init_failed:
            releasePage(pPage1);
            pBt.pPage1 = null;
            return rc;
        }

        /*
    ** If there are no outstanding cursors and we are not in the middle
    ** of a transaction but there is a read lock on the database, then
    ** this routine unrefs the first page of the database file which
    ** has the effect of releasing the read lock.
    **
    ** If there is a transaction in progress, this routine is a no-op.
    */

        private static void unlockBtreeIfUnused(BtShared pBt)
        {
            Debug.Assert(MutexHelper.MutexHeld(pBt.mutex));
            Debug.Assert(pBt.pCursor == null || pBt.inTransaction > TransactionType.TRANS_NONE);
            if (pBt.inTransaction == TransactionType.TRANS_NONE && pBt.pPage1 != null)
            {
                Debug.Assert(pBt.pPage1.aData != null);
                Debug.Assert(sqlite3PagerRefcount(pBt.pPager) == 1);
                Debug.Assert(pBt.pPage1.aData != null);
                releasePage(pBt.pPage1);
                pBt.pPage1 = null;
            }
        }

        /*
    ** If pBt points to an empty file then convert that empty file
    ** into a new empty database by initializing the first page of
    ** the database.
    */

        private static int newDatabase(BtShared pBt)
        {
            MemPage pP1;
            byte[] data;
            int rc;
            int nPage = 0;

            Debug.Assert(MutexHelper.MutexHeld(pBt.mutex));
            rc = sqlite3PagerPagecount(pBt.pPager, ref nPage);
            if (rc != StatusCode.SQLITE_OK || nPage > 0)
            {
                return rc;
            }
            pP1 = pBt.pPage1;
            Debug.Assert(pP1 != null);
            data = pP1.aData;
            rc = PagerHelper.PagerWrite(pP1.pDbPage);
            if (rc != 0) return rc;
            Buffer.BlockCopy(Encoding.UTF8.GetBytes(zMagicHeader), 0, data, 0, 16);
                // memcpy(data, zMagicHeader, sizeof(zMagicHeader));
            Debug.Assert(zMagicHeader.Length == 16);
            put2byte(data, 16, pBt.pageSize);
            data[18] = 1;
            data[19] = 1;
            Debug.Assert(pBt.usableSize <= pBt.pageSize && pBt.usableSize + 255 >= pBt.pageSize);
            data[20] = (byte) (pBt.pageSize - pBt.usableSize);
            data[21] = 64;
            data[22] = 32;
            data[23] = 32;
            //memset(&data[24], 0, 100-24);
            zeroPage(pP1, PageTypeFlag.PTF_INTKEY | PageTypeFlag.PTF_LEAF | PageTypeFlag.PTF_LEAFDATA);
            pBt.pageSizeFixed = true;
#if !SQLITE_OMIT_AUTOVACUUM
            Debug.Assert(pBt.autoVacuum || pBt.autoVacuum == false);
            Debug.Assert(pBt.incrVacuum || pBt.incrVacuum == false);
            Utility.Sqlite3Put4byte(data, 36 + 4*4, pBt.autoVacuum ? 1 : 0);
            Utility.Sqlite3Put4byte(data, 36 + 7*4, pBt.incrVacuum ? 1 : 0);
#endif
            return StatusCode.SQLITE_OK;
        }

        /*
    ** Attempt to start a new transaction. A write-transaction
    ** is started if the second argument is nonzero, otherwise a read-
    ** transaction.  If the second argument is 2 or more and exclusive
    ** transaction is started, meaning that no other process is allowed
    ** to access the database.  A preexisting transaction may not be
    ** upgraded to exclusive by calling this routine a second time - the
    ** exclusivity flag only works for a new transaction.
    **
    ** A write-transaction must be started before attempting any
    ** changes to the database.  None of the following routines
    ** will work unless a transaction is started first:
    **
    **      BTreeHelper.BtreeCreateTable()
    **      sqlite3BtreeCreateIndex()
    **      BTreeHelper.BtreeClearTable()
    **      BTreeHelper.BtreeDropTable()
    **      BTreeHelper.BtreeInsert()
    **      BTreeHelper.BtreeDelete()
    **      sqlite3BtreeUpdateMeta()
    **
    ** If an initial attempt to acquire the lock fails because of lock contention
    ** and the database was previously unlocked, then invoke the busy handler
    ** if there is one.  But if there was previously a read-lock, do not
    ** invoke the busy handler - just return StatusCode.SQLITE_BUSY.  StatusCode.SQLITE_BUSY is
    ** returned when there is already a read-lock in order to avoid a deadlock.
    **
    ** Suppose there are two processes A and B.  A has a read lock and B has
    ** a reserved lock.  B tries to promote to exclusive but is blocked because
    ** of A's read lock.  A tries to promote to reserved but is blocked by B.
    ** One or the other of the two processes must give way or there can be
    ** no progress.  By returning StatusCode.SQLITE_BUSY and not invoking the busy callback
    ** when A already has a read lock, we encourage A to give up and let B
    ** proceed.
    */

        private static int sqlite3BtreeBeginTrans(Btree p, int wrflag)
        {
            BtShared pBt = p.pBt;
            int rc = StatusCode.SQLITE_OK;

            sqlite3BtreeEnter(p);
            btreeIntegrity(p);

            /* If the btree is already in a write-transaction, or it
      ** is already in a read-transaction and a read-transaction
      ** is requested, this is a no-op.
      */
            if (p.inTrans == TransactionType.TRANS_WRITE || (p.inTrans == TransactionType.TRANS_READ && 0 == wrflag))
            {
                goto trans_begun;
            }

            /* Write transactions are not possible on a read-only database */
            if (pBt.readOnly && wrflag != 0)
            {
                rc = StatusCode.SQLITE_READONLY;
                goto trans_begun;
            }

#if !SQLITE_OMIT_SHARED_CACHE
/* If another database handle has already opened a write transaction
** on this shared-btree structure and a second write transaction is
** requested, return StatusCode.SQLITE_LOCKED.
*/
if( (wrflag && pBt.inTransaction==TransactionType.TRANS_WRITE) || pBt.isPending ){
sqlite3 pBlock = pBt.pWriter.db;
}else if( wrflag>1 ){
BtLock pIter;
for(pIter=pBt.pLock; pIter; pIter=pIter.pNext){
if( pIter.pBtree!=p ){
pBlock = pIter.pBtree.db;
break;
}
}
}
if( pBlock ){
sqlite3ConnectionBlocked(p.db, pBlock);
rc = ExtendedResultCode.SQLITE_LOCKED_SHAREDCACHE;
goto trans_begun;
}
#endif

            /* Any read-only or read-write transaction implies a read-lock on
** page 1. So if some other shared-cache client already has a write-lock
** on page 1, the transaction cannot be opened. */
            rc = querySharedCacheTableLock(p, Const.MASTER_ROOT, LockType.READ_LOCK);
            if (StatusCode.SQLITE_OK != rc) goto trans_begun;

            do
            {
                /* Call lockBtree() until either pBt.pPage1 is populated or
        ** lockBtree() returns something other than StatusCode.SQLITE_OK. lockBtree()
        ** may return StatusCode.SQLITE_OK but leave pBt.pPage1 set to 0 if after
        ** reading page 1 it discovers that the page-size of the database
        ** file is not pBt.pageSize. In this case lockBtree() will update
        ** pBt.pageSize to the page-size of the file on disk.
        */
                while (pBt.pPage1 == null && StatusCode.SQLITE_OK == (rc = lockBtree(pBt))) ;

                if (rc == StatusCode.SQLITE_OK && wrflag != 0)
                {
                    if (pBt.readOnly)
                    {
                        rc = StatusCode.SQLITE_READONLY;
                    }
                    else
                    {
                        rc = sqlite3PagerBegin(pBt.pPager, wrflag > 1, sqlite3TempInMemory(p.db) ? 1 : 0);
                        if (rc == StatusCode.SQLITE_OK)
                        {
                            rc = newDatabase(pBt);
                        }
                    }
                }

                if (rc != StatusCode.SQLITE_OK)
                {
                    unlockBtreeIfUnused(pBt);
                }
            } while (rc == StatusCode.SQLITE_BUSY && pBt.inTransaction == TransactionType.TRANS_NONE &&
                     btreeInvokeBusyHandler(pBt) != 0);

            if (rc == StatusCode.SQLITE_OK)
            {
                if (p.inTrans == TransactionType.TRANS_NONE)
                {
                    pBt.nTransaction++;
#if !SQLITE_OMIT_SHARED_CACHE
if( p.sharable ){
Debug.Assert( p.lock.pBtree==p && p.lock.iTable==1 );
p.lock.eLock = LockType.READ_LOCK;
p.lock.pNext = pBt.pLock;
pBt.pLock = &p.lock;
}
#endif
                }
                p.inTrans = (wrflag != 0 ? TransactionType.TRANS_WRITE : TransactionType.TRANS_READ);
                if (p.inTrans > pBt.inTransaction)
                {
                    pBt.inTransaction = p.inTrans;
                }
#if !SQLITE_OMIT_SHARED_CACHE
if( wrflag ){
Debug.Assert( !pBt.pWriter );
pBt.pWriter = p;
pBt.isExclusive = (byte)(wrflag>1);
}
#endif
            }


            trans_begun:
            if (rc == StatusCode.SQLITE_OK && wrflag != 0)
            {
                /* This call makes sure that the pager has the correct number of
        ** open savepoints. If the second parameter is greater than 0 and
        ** the sub-journal is not already open, then it will be opened here.
        */
                rc = sqlite3PagerOpenSavepoint(pBt.pPager, p.db.nSavepoint);
            }

            btreeIntegrity(p);
            sqlite3BtreeLeave(p);
            return rc;
        }

#if !SQLITE_OMIT_AUTOVACUUM

        /*
** Set the pointer-map entries for all children of page pPage. Also, if
** pPage contains cells that point to overflow pages, set the pointer
** map entries for the overflow pages as well.
*/

        private static int setChildPtrmaps(MemPage pPage)
        {
            int i; /* Counter variable */
            int nCell; /* Number of cells in page pPage */
            int rc; /* Return code */
            BtShared pBt = pPage.pBt;
            byte isInitOrig = pPage.isInit;
            uint pgno = pPage.pgno;

            Debug.Assert(MutexHelper.MutexHeld(pPage.pBt.mutex));
            rc = btreeInitPage(pPage);
            if (rc != StatusCode.SQLITE_OK)
            {
                goto set_child_ptrmaps_out;
            }
            nCell = pPage.nCell;

            for (i = 0; i < nCell; i++)
            {
                int pCell = findCell(pPage, i);

                ptrmapPutOvflPtr(pPage, pCell, ref rc);

                if (0 == pPage.leaf)
                {
                    uint childuint = Utility.Sqlite3Get4byte(pPage.aData, pCell);
                    ptrmapPut(pBt, childuint, PTRMAP_BTREE, pgno, ref rc);
                }
            }

            if (0 == pPage.leaf)
            {
                uint childuint = Utility.Sqlite3Get4byte(pPage.aData, pPage.hdrOffset + 8);
                ptrmapPut(pBt, childuint, PTRMAP_BTREE, pgno, ref rc);
            }

            set_child_ptrmaps_out:
            pPage.isInit = isInitOrig;
            return rc;
        }

        /*
    ** Somewhere on pPage is a pointer to page iFrom.  Modify this pointer so
    ** that it points to iTo. Parameter eType describes the type of pointer to
    ** be modified, as  follows:
    **
    ** PTRMAP_BTREE:     pPage is a btree-page. The pointer points at a child
    **                   page of pPage.
    **
    ** PTRMAP_OVERFLOW1: pPage is a btree-page. The pointer points at an overflow
    **                   page pointed to by one of the cells on pPage.
    **
    ** PTRMAP_OVERFLOW2: pPage is an overflow-page. The pointer points at the next
    **                   overflow page in the list.
    */

        private static int modifyPagePointer(MemPage pPage, uint iFrom, uint iTo, byte eType)
        {
            Debug.Assert(MutexHelper.MutexHeld(pPage.pBt.mutex));
            Debug.Assert(sqlite3PagerIswriteable(pPage.pDbPage));
            if (eType == PTRMAP_OVERFLOW2)
            {
                /* The pointer is always the first 4 bytes of the page in this case.  */
                if (Utility.Sqlite3Get4byte(pPage.aData) != iFrom)
                {
                    return UnitTest.SQLITE_CORRUPT_BKPT();
                }
                Utility.Sqlite3Put4byte(pPage.aData, iTo);
            }
            else
            {
                byte isInitOrig = pPage.isInit;
                int i;
                int nCell;

                btreeInitPage(pPage);
                nCell = pPage.nCell;

                for (i = 0; i < nCell; i++)
                {
                    int pCell = findCell(pPage, i);
                    if (eType == PTRMAP_OVERFLOW1)
                    {
                        var info = new CellInfo();
                        btreeParseCellPtr(pPage, pCell, ref info);
                        if (info.iOverflow != 0)
                        {
                            if (iFrom == Utility.Sqlite3Get4byte(pPage.aData, pCell, info.iOverflow))
                            {
                                Utility.Sqlite3Put4byte(pPage.aData, pCell + info.iOverflow, (int) iTo);
                                break;
                            }
                        }
                    }
                    else
                    {
                        if (Utility.Sqlite3Get4byte(pPage.aData, pCell) == iFrom)
                        {
                            Utility.Sqlite3Put4byte(pPage.aData, pCell, (int) iTo);
                            break;
                        }
                    }
                }

                if (i == nCell)
                {
                    if (eType != PTRMAP_BTREE ||
                        Utility.Sqlite3Get4byte(pPage.aData, pPage.hdrOffset + 8) != iFrom)
                    {
                        return UnitTest.SQLITE_CORRUPT_BKPT();
                    }
                    Utility.Sqlite3Put4byte(pPage.aData, pPage.hdrOffset + 8, iTo);
                }

                pPage.isInit = isInitOrig;
            }
            return StatusCode.SQLITE_OK;
        }


        /*
    ** Move the open database page pDbPage to location iFreePage in the
    ** database. The pDbPage reference remains valid.
    **
    ** The isCommit flag indicates that there is no need to remember that
    ** the journal needs to be sync()ed before database page pDbPage.pgno
    ** can be written to. The caller has already promised not to write to that
    ** page.
    */

        private static int relocatePage(
            BtShared pBt, /* Btree */
            MemPage pDbPage, /* Open page to move */
            byte eType, /* Pointer map 'type' entry for pDbPage */
            uint iPtrPage, /* Pointer map 'page-no' entry for pDbPage */
            uint iFreePage, /* The location to move pDbPage to */
            int isCommit /* isCommit flag passed to sqlite3PagerMovepage */
            )
        {
            var pPtrPage = new MemPage(); /* The page that contains a pointer to pDbPage */
            uint iDbPage = pDbPage.pgno;
            Pager pPager = pBt.pPager;
            int rc;

            Debug.Assert(eType == PTRMAP_OVERFLOW2 || eType == PTRMAP_OVERFLOW1 ||
                         eType == PTRMAP_BTREE || eType == PTRMAP_ROOTPAGE);
            Debug.Assert(MutexHelper.MutexHeld(pBt.mutex));
            Debug.Assert(pDbPage.pBt == pBt);

            /* Move page iDbPage from its current location to page number iFreePage */
            TRACE("AUTOVACUUM: Moving %d to free page %d (ptr page %d type %d)\n",
                  iDbPage, iFreePage, iPtrPage, eType);
            rc = sqlite3PagerMovepage(pPager, pDbPage.pDbPage, iFreePage, isCommit);
            if (rc != StatusCode.SQLITE_OK)
            {
                return rc;
            }
            pDbPage.pgno = iFreePage;

            /* If pDbPage was a btree-page, then it may have child pages and/or cells
      ** that point to overflow pages. The pointer map entries for all these
      ** pages need to be changed.
      **
      ** If pDbPage is an overflow page, then the first 4 bytes may store a
      ** pointer to a subsequent overflow page. If this is the case, then
      ** the pointer map needs to be updated for the subsequent overflow page.
      */
            if (eType == PTRMAP_BTREE || eType == PTRMAP_ROOTPAGE)
            {
                rc = setChildPtrmaps(pDbPage);
                if (rc != StatusCode.SQLITE_OK)
                {
                    return rc;
                }
            }
            else
            {
                uint nextOvfl = Utility.Sqlite3Get4byte(pDbPage.aData);
                if (nextOvfl != 0)
                {
                    ptrmapPut(pBt, nextOvfl, PTRMAP_OVERFLOW2, iFreePage, ref rc);
                    if (rc != StatusCode.SQLITE_OK)
                    {
                        return rc;
                    }
                }
            }

            /* Fix the database pointer on page iPtrPage that pointed at iDbPage so
      ** that it points at iFreePage. Also fix the pointer map entry for
      ** iPtrPage.
      */
            if (eType != PTRMAP_ROOTPAGE)
            {
                rc = btreeGetPage(pBt, iPtrPage, ref pPtrPage, 0);
                if (rc != StatusCode.SQLITE_OK)
                {
                    return rc;
                }
                rc = PagerHelper.PagerWrite(pPtrPage.pDbPage);
                if (rc != StatusCode.SQLITE_OK)
                {
                    releasePage(pPtrPage);
                    return rc;
                }
                rc = modifyPagePointer(pPtrPage, iDbPage, iFreePage, eType);
                releasePage(pPtrPage);
                if (rc == StatusCode.SQLITE_OK)
                {
                    ptrmapPut(pBt, iFreePage, eType, iPtrPage, ref rc);
                }
            }
            return rc;
        }

        /* Forward declaration required by incrVacuumStep(). */
        //static int allocateBtreePage(BtShared *, MemPage **, uint *, uint, byte);

        /*
    ** Perform a single step of an incremental-vacuum. If successful,
    ** return StatusCode.SQLITE_OK. If there is no work to do (and therefore no
    ** point in calling this function again), return StatusCode.SQLITE_DONE.
    **
    ** More specificly, this function attempts to re-organize the
    ** database so that the last page of the file currently in use
    ** is no longer in use.
    **
    ** If the nFin parameter is non-zero, this function assumes
    ** that the caller will keep calling incrVacuumStep() until
    ** it returns StatusCode.SQLITE_DONE or an error, and that nFin is the
    ** number of pages the database file will contain after this
    ** process is complete.  If nFin is zero, it is assumed that
    ** incrVacuumStep() will be called a finite amount of times
    ** which may or may not empty the freelist.  A full autovacuum
    ** has nFin>0.  A "PRAGMA incremental_vacuum" has nFin==null.
    */

        private static int incrVacuumStep(BtShared pBt, uint nFin, uint iLastPg)
        {
            uint nFreeList; /* Number of pages still on the free-list */

            Debug.Assert(MutexHelper.MutexHeld(pBt.mutex));
            Debug.Assert(iLastPg > nFin);

            if (!PTRMAP_ISPAGE(pBt, iLastPg) && iLastPg != Utility.PENDING_BYTE_PAGE(pBt))
            {
                int rc;
                byte eType = 0;
                uint iPtrPage = 0;

                nFreeList = Utility.Sqlite3Get4byte(pBt.pPage1.aData, 36);
                if (nFreeList == 0)
                {
                    return StatusCode.SQLITE_DONE;
                }

                rc = ptrmapGet(pBt, iLastPg, ref eType, ref iPtrPage);
                if (rc != StatusCode.SQLITE_OK)
                {
                    return rc;
                }
                if (eType == PTRMAP_ROOTPAGE)
                {
                    return UnitTest.SQLITE_CORRUPT_BKPT();
                }

                if (eType == PTRMAP_FREEPAGE)
                {
                    if (nFin == 0)
                    {
                        /* Remove the page from the files free-list. This is not required
            ** if nFin is non-zero. In that case, the free-list will be
            ** truncated to zero after this function returns, so it doesn't
            ** matter if it still contains some garbage entries.
            */
                        uint iFreePg = 0;
                        var pFreePg = new MemPage();
                        rc = allocateBtreePage(pBt, ref pFreePg, ref iFreePg, iLastPg, 1);
                        if (rc != StatusCode.SQLITE_OK)
                        {
                            return rc;
                        }
                        Debug.Assert(iFreePg == iLastPg);
                        releasePage(pFreePg);
                    }
                }
                else
                {
                    uint iFreePg = 0; /* Index of free page to move pLastPg to */
                    var pLastPg = new MemPage();

                    rc = btreeGetPage(pBt, iLastPg, ref pLastPg, 0);
                    if (rc != StatusCode.SQLITE_OK)
                    {
                        return rc;
                    }

                    /* If nFin is zero, this loop runs exactly once and page pLastPg
          ** is swapped with the first free page pulled off the free list.
          **
          ** On the other hand, if nFin is greater than zero, then keep
          ** looping until a free-page located within the first nFin pages
          ** of the file is found.
          */
                    do
                    {
                        var pFreePg = new MemPage();
                        rc = allocateBtreePage(pBt, ref pFreePg, ref iFreePg, 0, 0);
                        if (rc != StatusCode.SQLITE_OK)
                        {
                            releasePage(pLastPg);
                            return rc;
                        }
                        releasePage(pFreePg);
                    } while (nFin != 0 && iFreePg > nFin);
                    Debug.Assert(iFreePg < iLastPg);

                    rc = PagerHelper.PagerWrite(pLastPg.pDbPage);
                    if (rc == StatusCode.SQLITE_OK)
                    {
                        rc = relocatePage(pBt, pLastPg, eType, iPtrPage, iFreePg, (nFin != 0) ? 1 : 0);
                    }
                    releasePage(pLastPg);
                    if (rc != StatusCode.SQLITE_OK)
                    {
                        return rc;
                    }
                }
            }

            if (nFin == 0)
            {
                iLastPg--;
                while (iLastPg == Utility.PENDING_BYTE_PAGE(pBt) || PTRMAP_ISPAGE(pBt, iLastPg))
                {
                    if (PTRMAP_ISPAGE(pBt, iLastPg))
                    {
                        var pPg = new MemPage();
                        int rc = btreeGetPage(pBt, iLastPg, ref pPg, 0);
                        if (rc != StatusCode.SQLITE_OK)
                        {
                            return rc;
                        }
                        rc = PagerHelper.PagerWrite(pPg.pDbPage);
                        releasePage(pPg);
                        if (rc != StatusCode.SQLITE_OK)
                        {
                            return rc;
                        }
                    }
                    iLastPg--;
                }
                sqlite3PagerTruncateImage(pBt.pPager, iLastPg);
            }
            return StatusCode.SQLITE_OK;
        }

        /*
    ** A write-transaction must be opened before calling this function.
    ** It performs a single unit of work towards an incremental vacuum.
    **
    ** If the incremental vacuum is finished after this function has run,
    ** StatusCode.SQLITE_DONE is returned. If it is not finished, but no error occurred,
    ** StatusCode.SQLITE_OK is returned. Otherwise an SQLite error code.
    */

        private static int sqlite3BtreeIncrVacuum(Btree p)
        {
            int rc;
            BtShared pBt = p.pBt;

            sqlite3BtreeEnter(p);
            Debug.Assert(pBt.inTransaction == TransactionType.TRANS_WRITE && p.inTrans == TransactionType.TRANS_WRITE);
            if (!pBt.autoVacuum)
            {
                rc = StatusCode.SQLITE_DONE;
            }
            else
            {
                invalidateAllOverflowCache(pBt);
                rc = incrVacuumStep(pBt, 0, pagerPagecount(pBt));
            }
            sqlite3BtreeLeave(p);
            return rc;
        }

        /*
    ** This routine is called prior to sqlite3PagerCommit when a transaction
    ** is commited for an auto-vacuum database.
    **
    ** If StatusCode.SQLITE_OK is returned, then pnTrunc is set to the number of pages
    ** the database file should be truncated to during the commit process.
    ** i.e. the database has been reorganized so that only the first pnTrunc
    ** pages are in use.
    */

        private static int autoVacuumCommit(BtShared pBt)
        {
            int rc = StatusCode.SQLITE_OK;
            Pager pPager = pBt.pPager;
            // VVA_ONLY( int nRef = sqlite3PagerRefcount(pPager) );
#if !NDEBUG || DEBUG
            int nRef = sqlite3PagerRefcount(pPager);
#else
int nRef=0;
#endif


            Debug.Assert(MutexHelper.MutexHeld(pBt.mutex));
            invalidateAllOverflowCache(pBt);
            Debug.Assert(pBt.autoVacuum);
            if (!pBt.incrVacuum)
            {
                uint nFin; /* Number of pages in database after autovacuuming */
                uint nFree; /* Number of pages on the freelist initially */
                uint nPtrmap; /* Number of PtrMap pages to be freed */
                uint iFree; /* The next page to be freed */
                int nEntry; /* Number of entries on one ptrmap page */
                uint nOrig; /* Database size before freeing */

                nOrig = pagerPagecount(pBt);
                if (PTRMAP_ISPAGE(pBt, nOrig) || nOrig == Utility.PENDING_BYTE_PAGE(pBt))
                {
                    /* It is not possible to create a database for which the final page
          ** is either a pointer-map page or the pending-byte page. If one
          ** is encountered, this indicates corruption.
          */
                    return UnitTest.SQLITE_CORRUPT_BKPT();
                }

                nFree = Utility.Sqlite3Get4byte(pBt.pPage1.aData, 36);
                nEntry = pBt.usableSize/5;
                nPtrmap = (uint) ((nFree - nOrig + PTRMAP_PAGENO(pBt, nOrig) + (uint) nEntry)/nEntry);
                nFin = nOrig - nFree - nPtrmap;
                if (nOrig > Utility.PENDING_BYTE_PAGE(pBt) && nFin < Utility.PENDING_BYTE_PAGE(pBt))
                {
                    nFin--;
                }
                while (PTRMAP_ISPAGE(pBt, nFin) || nFin == Utility.PENDING_BYTE_PAGE(pBt))
                {
                    nFin--;
                }
                if (nFin > nOrig)
                    return UnitTest.SQLITE_CORRUPT_BKPT();

                for (iFree = nOrig; iFree > nFin && rc == StatusCode.SQLITE_OK; iFree--)
                {
                    rc = incrVacuumStep(pBt, nFin, iFree);
                }
                if ((rc == StatusCode.SQLITE_DONE || rc == StatusCode.SQLITE_OK) && nFree > 0)
                {
                    rc = StatusCode.SQLITE_OK;
                    rc = PagerHelper.PagerWrite(pBt.pPage1.pDbPage);
                    Utility.Sqlite3Put4byte(pBt.pPage1.aData, 32, 0);
                    Utility.Sqlite3Put4byte(pBt.pPage1.aData, 36, 0);
                    sqlite3PagerTruncateImage(pBt.pPager, nFin);
                }
                if (rc != StatusCode.SQLITE_OK)
                {
                    sqlite3PagerRollback(pPager);
                }
            }

            Debug.Assert(nRef == sqlite3PagerRefcount(pPager));
            return rc;
        }

#else //* ifndef SQLITE_OMIT_AUTOVACUUM */
//# define setChildPtrmaps(x) StatusCode.SQLITE_OK
#endif

        /*
** This routine does the first phase of a two-phase commit.  This routine
** causes a rollback journal to be created (if it does not already exist)
** and populated with enough information so that if a power loss occurs
** the database can be restored to its original state by playing back
** the journal.  Then the contents of the journal are flushed out to
** the disk.  After the journal is safely on oxide, the changes to the
** database are written into the database file and flushed to oxide.
** At the end of this call, the rollback journal still exists on the
** disk and we are still holding all locks, so the transaction has not
** committed.  See sqlite3BtreeCommitPhaseTwo() for the second phase of the
** commit process.
**
** This call is a no-op if no write-transaction is currently active on pBt.
**
** Otherwise, sync the database file for the btree pBt. zMaster points to
** the name of a master journal file that should be written into the
** individual journal file, or is NULL, indicating no master journal file
** (single database transaction).
**
** When this is called, the master journal should already have been
** created, populated with this journal pointer and synced to disk.
**
** Once this is routine has returned, the only thing required to commit
** the write-transaction for this database file is to delete the journal.
*/

        private static int sqlite3BtreeCommitPhaseOne(Btree p, string zMaster)
        {
            int rc = StatusCode.SQLITE_OK;
            if (p.inTrans == TransactionType.TRANS_WRITE)
            {
                BtShared pBt = p.pBt;
                sqlite3BtreeEnter(p);
#if !SQLITE_OMIT_AUTOVACUUM
                if (pBt.autoVacuum)
                {
                    rc = autoVacuumCommit(pBt);
                    if (rc != StatusCode.SQLITE_OK)
                    {
                        sqlite3BtreeLeave(p);
                        return rc;
                    }
                }
#endif
                rc = sqlite3PagerCommitPhaseOne(pBt.pPager, zMaster, false);
                sqlite3BtreeLeave(p);
            }
            return rc;
        }

        /*
    ** This function is called from both BtreeCommitPhaseTwo() and BtreeRollback()
    ** at the conclusion of a transaction.
    */

        private static void btreeEndTransaction(Btree p)
        {
            BtShared pBt = p.pBt;
            Debug.Assert(sqlite3BtreeHoldsMutex(p));

            btreeClearHasContent(pBt);
            if (p.inTrans > TransactionType.TRANS_NONE && p.db.activeVdbeCnt > 1)
            {
                /* If there are other active statements that belong to this database
        ** handle, downgrade to a read-only transaction. The other statements
        ** may still be reading from the database.  */

                downgradeAllSharedCacheTableLocks(p);
                p.inTrans = TransactionType.TRANS_READ;
            }
            else
            {
                /* If the handle had any kind of transaction open, decrement the
        ** transaction count of the shared btree. If the transaction count
        ** reaches 0, set the shared state to TransactionType.TRANS_NONE. The unlockBtreeIfUnused()
        ** call below will unlock the pager.  */
                if (p.inTrans != TransactionType.TRANS_NONE)
                {
                    clearAllSharedCacheTableLocks(p);
                    pBt.nTransaction--;
                    if (0 == pBt.nTransaction)
                    {
                        pBt.inTransaction = TransactionType.TRANS_NONE;
                    }
                }

                /* Set the current transaction state to TransactionType.TRANS_NONE and unlock the
        ** pager if this call closed the only read or write transaction.  */
                p.inTrans = TransactionType.TRANS_NONE;
                unlockBtreeIfUnused(pBt);
            }

            btreeIntegrity(p);
        }

        /*
    ** Commit the transaction currently in progress.
    **
    ** This routine implements the second phase of a 2-phase commit.  The
    ** sqlite3BtreeCommitPhaseOne() routine does the first phase and should
    ** be invoked prior to calling this routine.  The sqlite3BtreeCommitPhaseOne()
    ** routine did all the work of writing information out to disk and flushing the
    ** contents so that they are written onto the disk platter.  All this
    ** routine has to do is delete or truncate or zero the header in the
    ** the rollback journal (which causes the transaction to commit) and
    ** drop locks.
    **
    ** This will release the write lock on the database file.  If there
    ** are no active cursors, it also releases the read lock.
    */

        private static int sqlite3BtreeCommitPhaseTwo(Btree p)
        {
            BtShared pBt = p.pBt;

            sqlite3BtreeEnter(p);
            btreeIntegrity(p);

            /* If the handle has a write-transaction open, commit the shared-btrees
      ** transaction and set the shared state to TransactionType.TRANS_READ.
      */
            if (p.inTrans == TransactionType.TRANS_WRITE)
            {
                int rc;
                Debug.Assert(pBt.inTransaction == TransactionType.TRANS_WRITE);
                Debug.Assert(pBt.nTransaction > 0);
                rc = sqlite3PagerCommitPhaseTwo(pBt.pPager);
                if (rc != StatusCode.SQLITE_OK)
                {
                    sqlite3BtreeLeave(p);
                    return rc;
                }
                pBt.inTransaction = TransactionType.TRANS_READ;
            }

            btreeEndTransaction(p);
            sqlite3BtreeLeave(p);
            return StatusCode.SQLITE_OK;
        }

        /*
    ** Do both phases of a commit.
    */

        private static int sqlite3BtreeCommit(Btree p)
        {
            int rc;
            sqlite3BtreeEnter(p);
            rc = sqlite3BtreeCommitPhaseOne(p, null);
            if (rc == StatusCode.SQLITE_OK)
            {
                rc = sqlite3BtreeCommitPhaseTwo(p);
            }
            sqlite3BtreeLeave(p);
            return rc;
        }

#if !NDEBUG || DEBUG
        /*
** Return the number of write-cursors open on this handle. This is for use
** in Debug.Assert() expressions, so it is only compiled if NDEBUG is not
** defined.
**
** For the purposes of this routine, a write-cursor is any cursor that
** is capable of writing to the databse.  That means the cursor was
** originally opened for writing and the cursor has not be disabled
** by having its state changed to CursorFlag.CURSOR_FAULT.
*/

        private static int countWriteCursors(BtShared pBt)
        {
            BtCursor pCur;
            int r = 0;
            for (pCur = pBt.pCursor; pCur != null; pCur = pCur.pNext)
            {
                if (pCur.wrFlag != 0 && pCur.eState != CursorFlag.CURSOR_FAULT) r++;
            }
            return r;
        }
#else
static int countWriteCursors(BtShared pBt) { return -1; }
#endif

        /*
** This routine sets the state to CursorFlag.CURSOR_FAULT and the error
** code to errCode for every cursor on BtShared that pBtree
** references.
**
** Every cursor is tripped, including cursors that belong
** to other database connections that happen to be sharing
** the cache with pBtree.
**
** This routine gets called when a rollback occurs.
** All cursors using the same cache must be tripped
** to prevent them from trying to use the btree after
** the rollback.  The rollback may have deleted tables
** or moved root pages, so it is not sufficient to
** save the state of the cursor.  The cursor must be
** invalidated.
*/

        private static void sqlite3BtreeTripAllCursors(Btree pBtree, int errCode)
        {
            BtCursor p;
            sqlite3BtreeEnter(pBtree);
            for (p = pBtree.pBt.pCursor; p != null; p = p.pNext)
            {
                int i;
                sqlite3BtreeClearCursor(p);
                p.eState = CursorFlag.CURSOR_FAULT;
                p.skipNext = errCode;
                for (i = 0; i <= p.iPage; i++)
                {
                    releasePage(p.apPage[i]);
                    p.apPage[i] = null;
                }
            }
            sqlite3BtreeLeave(pBtree);
        }

        /*
    ** Rollback the transaction in progress.  All cursors will be
    ** invalided by this operation.  Any attempt to use a cursor
    ** that was open at the beginning of this operation will result
    ** in an error.
    **
    ** This will release the write lock on the database file.  If there
    ** are no active cursors, it also releases the read lock.
    */

        private static int sqlite3BtreeRollback(Btree p)
        {
            int rc;
            BtShared pBt = p.pBt;
            var pPage1 = new MemPage();

            sqlite3BtreeEnter(p);
            rc = saveAllCursors(pBt, 0, null);
#if !SQLITE_OMIT_SHARED_CACHE
if( rc!=StatusCode.SQLITE_OK ){
/* This is a horrible situation. An IO or malloc() error occurred whilst
** trying to save cursor positions. If this is an automatic rollback (as
** the result of a constraint, malloc() failure or IO error) then
** the cache may be internally inconsistent (not contain valid trees) so
** we cannot simply return the error to the caller. Instead, abort
** all queries that may be using any of the cursors that failed to save.
*/
sqlite3BtreeTripAllCursors(p, rc);
}
#endif
            btreeIntegrity(p);

            if (p.inTrans == TransactionType.TRANS_WRITE)
            {
                int rc2;

                Debug.Assert(TransactionType.TRANS_WRITE == pBt.inTransaction);
                rc2 = sqlite3PagerRollback(pBt.pPager);
                if (rc2 != StatusCode.SQLITE_OK)
                {
                    rc = rc2;
                }

                /* The rollback may have destroyed the pPage1.aData value.  So
        ** call btreeGetPage() on page 1 again to make
        ** sure pPage1.aData is set correctly. */
                if (btreeGetPage(pBt, 1, ref pPage1, 0) == StatusCode.SQLITE_OK)
                {
                    releasePage(pPage1);
                }
                Debug.Assert(countWriteCursors(pBt) == 0);
                pBt.inTransaction = TransactionType.TRANS_READ;
            }

            btreeEndTransaction(p);
            sqlite3BtreeLeave(p);
            return rc;
        }

        /*
    ** Start a statement subtransaction. The subtransaction can can be rolled
    ** back independently of the main transaction. You must start a transaction
    ** before starting a subtransaction. The subtransaction is ended automatically
    ** if the main transaction commits or rolls back.
    **
    ** Statement subtransactions are used around individual SQL statements
    ** that are contained within a BEGIN...COMMIT block.  If a constraint
    ** error occurs within the statement, the effect of that one statement
    ** can be rolled back without having to rollback the entire transaction.
    **
    ** A statement sub-transaction is implemented as an anonymous savepoint. The
    ** value passed as the second parameter is the total number of savepoints,
    ** including the new anonymous savepoint, open on the B-Tree. i.e. if there
    ** are no active savepoints and no other statement-transactions open,
    ** iStatement is 1. This anonymous savepoint can be released or rolled back
    ** using the sqlite3BtreeSavepoint() function.
    */

        private static int sqlite3BtreeBeginStmt(Btree p, int iStatement)
        {
            int rc;
            BtShared pBt = p.pBt;
            sqlite3BtreeEnter(p);
            Debug.Assert(p.inTrans == TransactionType.TRANS_WRITE);
            Debug.Assert(!pBt.readOnly);
            Debug.Assert(iStatement > 0);
            Debug.Assert(iStatement > p.db.nSavepoint);
            if (UnitTest.NEVER(p.inTrans != TransactionType.TRANS_WRITE || pBt.readOnly))
            {
                rc = StatusCode.SQLITE_INTERNAL;
            }
            else
            {
                Debug.Assert(pBt.inTransaction == TransactionType.TRANS_WRITE);
                /* At the pager level, a statement transaction is a savepoint with
        ** an index greater than all savepoints created explicitly using
        ** SQL statements. It is illegal to open, release or rollback any
        ** such savepoints while the statement transaction savepoint is active.
        */
                rc = sqlite3PagerOpenSavepoint(pBt.pPager, iStatement);
            }
            sqlite3BtreeLeave(p);
            return rc;
        }

        /*
    ** The second argument to this function, op, is always SavePoint.SAVEPOINT_ROLLBACK
    ** or SavePoint.SAVEPOINT_RELEASE. This function either releases or rolls back the
    ** savepoint identified by parameter iSavepoint, depending on the value
    ** of op.
    **
    ** Normally, iSavepoint is greater than or equal to zero. However, if op is
    ** SavePoint.SAVEPOINT_ROLLBACK, then iSavepoint may also be -1. In this case the
    ** contents of the entire transaction are rolled back. This is different
    ** from a normal transaction rollback, as no locks are released and the
    ** transaction remains open.
    */

        private static int sqlite3BtreeSavepoint(Btree p, int op, int iSavepoint)
        {
            int rc = StatusCode.SQLITE_OK;
            if (p != null && p.inTrans == TransactionType.TRANS_WRITE)
            {
                BtShared pBt = p.pBt;
                Debug.Assert(op == SavePoint.SAVEPOINT_RELEASE || op == SavePoint.SAVEPOINT_ROLLBACK);
                Debug.Assert(iSavepoint >= 0 || (iSavepoint == -1 && op == SavePoint.SAVEPOINT_ROLLBACK));
                sqlite3BtreeEnter(p);
                rc = sqlite3PagerSavepoint(pBt.pPager, op, iSavepoint);
                if (rc == StatusCode.SQLITE_OK)
                {
                    rc = newDatabase(pBt);
                }
                sqlite3BtreeLeave(p);
            }
            return rc;
        }

        /*
    ** Create a new cursor for the BTree whose root is on the page
    ** iTable. If a read-only cursor is requested, it is assumed that
    ** the caller already has at least a read-only transaction open
    ** on the database already. If a write-cursor is requested, then
    ** the caller is assumed to have an open write transaction.
    **
    ** If wrFlag==null, then the cursor can only be used for reading.
    ** If wrFlag==1, then the cursor can be used for reading or for
    ** writing if other conditions for writing are also met.  These
    ** are the conditions that must be met in order for writing to
    ** be allowed:
    **
    ** 1:  The cursor must have been opened with wrFlag==1
    **
    ** 2:  Other database connections that share the same pager cache
    **     but which are not in the READ_UNCOMMITTED state may not have
    **     cursors open with wrFlag==null on the same table.  Otherwise
    **     the changes made by this write cursor would be visible to
    **     the read cursors in the other database connection.
    **
    ** 3:  The database must be writable (not on read-only media)
    **
    ** 4:  There must be an active transaction.
    **
    ** No checking is done to make sure that page iTable really is the
    ** root page of a b-tree.  If it is not, then the cursor acquired
    ** will not work correctly.
    **
    ** It is assumed that the sqlite3BtreeCursorZero() has been called
    ** on pCur to initialize the memory space prior to invoking this routine.
    */

        private static int btreeCursor(
            Btree p, /* The btree */
            int iTable, /* Root page of table to open */
            int wrFlag, /* 1 to write. 0 read-only */
            KeyInfo pKeyInfo, /* First arg to comparison function */
            BtCursor pCur /* Space for new cursor */
            )
        {
            BtShared pBt = p.pBt; /* Shared b-tree handle */

            Debug.Assert(sqlite3BtreeHoldsMutex(p));
            Debug.Assert(wrFlag == 0 || wrFlag == 1);

            /* The following Debug.Assert statements verify that if this is a sharable
      ** b-tree database, the connection is holding the required table locks,
      ** and that no other connection has any open cursor that conflicts with
      ** this lock.  */
            Debug.Assert(hasSharedCacheTableLock(p, (uint) iTable, pKeyInfo != null ? 1 : 0, wrFlag + 1));
            Debug.Assert(wrFlag == 0 || !hasReadConflicts(p, (uint) iTable));

            /* Assert that the caller has opened the required transaction. */
            Debug.Assert(p.inTrans > TransactionType.TRANS_NONE);
            Debug.Assert(wrFlag == 0 || p.inTrans == TransactionType.TRANS_WRITE);
            Debug.Assert(pBt.pPage1 != null && pBt.pPage1.aData != null);

            if (UnitTest.NEVER(wrFlag != 0 && pBt.readOnly))
            {
                return StatusCode.SQLITE_READONLY;
            }
            if (iTable == 1 && pagerPagecount(pBt) == 0)
            {
                return StatusCode.SQLITE_EMPTY;
            }

            /* Now that no other errors can occur, finish filling in the BtCursor
      ** variables and link the cursor into the BtShared list.  */
            pCur.pgnoRoot = (uint) iTable;
            pCur.iPage = -1;
            pCur.pKeyInfo = pKeyInfo;
            pCur.pBtree = p;
            pCur.pBt = pBt;
            pCur.wrFlag = (byte) wrFlag;
            pCur.pNext = pBt.pCursor;
            if (pCur.pNext != null)
            {
                pCur.pNext.pPrev = pCur;
            }
            pBt.pCursor = pCur;
            pCur.eState = CursorFlag.CURSOR_INVALID;
            pCur.cachedRowid = 0;
            return StatusCode.SQLITE_OK;
        }

        private static int sqlite3BtreeCursor(
            Btree p, /* The btree */
            int iTable, /* Root page of table to open */
            int wrFlag, /* 1 to write. 0 read-only */
            KeyInfo pKeyInfo, /* First arg to xCompare() */
            BtCursor pCur /* Write new cursor here */
            )
        {
            int rc;
            sqlite3BtreeEnter(p);
            rc = btreeCursor(p, iTable, wrFlag, pKeyInfo, pCur);
            sqlite3BtreeLeave(p);
            return rc;
        }

        /*
    ** Return the size of a BtCursor object in bytes.
    **
    ** This interfaces is needed so that users of cursors can preallocate
    ** sufficient storage to hold a cursor.  The BtCursor object is opaque
    ** to users so they cannot do the sizeof() themselves - they must call
    ** this routine.
    */

        private static int sqlite3BtreeCursorSize()
        {
            return -1; // Not Used --  return Utility.ROUND8(sizeof(BtCursor));
        }

        /*
    ** Initialize memory that will be converted into a BtCursor object.
    **
    ** The simple approach here would be to memset() the entire object
    ** to zero.  But it turns out that the apPage[] and aiIdx[] arrays
    ** do not need to be zeroed and they are large, so we can save a lot
    ** of run-time by skipping the initialization of those elements.
    */

        private static void sqlite3BtreeCursorZero(BtCursor p)
        {
            p.Clear(); // memset( p, 0, offsetof( BtCursor, iPage ) );
        }

        /*
    ** Set the cached rowid value of every cursor in the same database file
    ** as pCur and having the same root page number as pCur.  The value is
    ** set to iRowid.
    **
    ** Only positive rowid values are considered valid for this cache.
    ** The cache is initialized to zero, indicating an invalid cache.
    ** A btree will work fine with zero or negative rowids.  We just cannot
    ** cache zero or negative rowids, which means tables that use zero or
    ** negative rowids might run a little slower.  But in practice, zero
    ** or negative rowids are very uncommon so this should not be a problem.
    */

        private static void sqlite3BtreeSetCachedRowid(BtCursor pCur, long iRowid)
        {
            BtCursor p;
            for (p = pCur.pBt.pCursor; p != null; p = p.pNext)
            {
                if (p.pgnoRoot == pCur.pgnoRoot) p.cachedRowid = iRowid;
            }
            Debug.Assert(pCur.cachedRowid == iRowid);
        }

        /*
    ** Return the cached rowid for the given cursor.  A negative or zero
    ** return value indicates that the rowid cache is invalid and should be
    ** ignored.  If the rowid cache has never before been set, then a
    ** zero is returned.
    */

        private static long sqlite3BtreeGetCachedRowid(BtCursor pCur)
        {
            return pCur.cachedRowid;
        }

        /*
    ** Close a cursor.  The read lock on the database file is released
    ** when the last cursor is closed.
    */

        private static int sqlite3BtreeCloseCursor(BtCursor pCur)
        {
            Btree pBtree = pCur.pBtree;
            if (pBtree != null)
            {
                int i;
                BtShared pBt = pCur.pBt;
                sqlite3BtreeEnter(pBtree);
                sqlite3BtreeClearCursor(pCur);
                if (pCur.pPrev != null)
                {
                    pCur.pPrev.pNext = pCur.pNext;
                }
                else
                {
                    pBt.pCursor = pCur.pNext;
                }
                if (pCur.pNext != null)
                {
                    pCur.pNext.pPrev = pCur.pPrev;
                }
                for (i = 0; i <= pCur.iPage; i++)
                {
                    releasePage(pCur.apPage[i]);
                }
                unlockBtreeIfUnused(pBt);
                invalidateOverflowCache(pCur);
                /* Malloc.sqlite3_free(ref pCur); */
                sqlite3BtreeLeave(pBtree);
            }
            return StatusCode.SQLITE_OK;
        }

        /*
    ** Make sure the BtCursor* given in the argument has a valid
    ** BtCursor.info structure.  If it is not already valid, call
    ** btreeParseCell() to fill it in.
    **
    ** BtCursor.info is a cache of the information in the current cell.
    ** Using this cache reduces the number of calls to btreeParseCell().
    **
    ** 2007-06-25:  There is a bug in some versions of MSVC that cause the
    ** compiler to crash when getCellInfo() is implemented as a macro.
    ** But there is a measureable speed advantage to using the macro on gcc
    ** (when less compiler optimizations like -Os or -O0 are used and the
    ** compiler is not doing agressive inlining.)  So we use a real function
    ** for MSVC and a macro for everything else.  Ticket #2457.
    */
#if !NDEBUG
    static void assertCellInfo( BtCursor pCur )
    {
      CellInfo info;
      int iPage = pCur.iPage;
      info = new CellInfo();//memset(info, 0, sizeof(info));
      btreeParseCell( pCur.apPage[iPage], pCur.aiIdx[iPage], ref info );
      Debug.Assert( info.GetHashCode() == pCur.info.GetHashCode() || info.Equals( pCur.info ) );//Custom.Memcmp(info, pCur.info, sizeof(info))==0 );
    }
#else
//  #define assertCellInfo(x)
        private static void assertCellInfo(BtCursor pCur)
        {
        }
#endif
#if _MSC_VER
        /* Use a real function in MSVC to work around bugs in that compiler. */

        private static void getCellInfo(BtCursor pCur)
        {
            if (pCur.info.nSize == 0)
            {
                int iPage = pCur.iPage;
                btreeParseCell(pCur.apPage[iPage], pCur.aiIdx[iPage], ref pCur.info);
                pCur.validNKey = true;
            }
            else
            {
                assertCellInfo(pCur);
            }
        }
#else //* if not _MSC_VER */
/* Use a macro in all other compilers so that the function is inlined */
//#define getCellInfo(pCur)                                                      \
//  if( pCur.info.nSize==null ){                                                   \
//    int iPage = pCur.iPage;                                                   \
//    btreeParseCell(pCur.apPage[iPage],pCur.aiIdx[iPage],&pCur.info); \
//    pCur.validNKey = true;                                                       \
//  }else{                                                                       \
//    assertCellInfo(pCur);                                                      \
//  }
#endif
        //* _MSC_VER */

#if !NDEBUG //* The next routine used only within Debug.Assert() statements */
    /*
** Return true if the given BtCursor is valid.  A valid cursor is one
** that is currently pointing to a row in a (non-empty) table.
** This is a verification routine is used only within Debug.Assert() statements.
*/
    static bool BTreeHelper.BtreeCursorIsValid( BtCursor pCur )
    {
      return pCur != null && pCur.eState == CursorFlag.CURSOR_VALID;
    }
#else
        public static bool BtreeCursorIsValid(BtCursor pCur)
        {
            return true;
        }
#endif
        //* NDEBUG */

        /*
** Set pSize to the size of the buffer needed to hold the value of
** the key for the current entry.  If the cursor is not pointing
** to a valid entry, pSize is set to 0.
**
** For a table with the INTKEY flag set, this routine returns the key
** itself, not the number of bytes in the key.
**
** The caller must position the cursor prior to invoking this routine.
**
** This routine cannot fail.  It always returns StatusCode.SQLITE_OK.
*/

        public static int BtreeKeySize(BtCursor pCur, ref long pSize)
        {
            Debug.Assert(cursorHoldsMutex(pCur));
            Debug.Assert(pCur.eState == CursorFlag.CURSOR_INVALID || pCur.eState == CursorFlag.CURSOR_VALID);
            if (pCur.eState != CursorFlag.CURSOR_VALID)
            {
                pSize = 0;
            }
            else
            {
                getCellInfo(pCur);
                pSize = pCur.info.nKey;
            }
            return StatusCode.SQLITE_OK;
        }

        /*
    ** Set pSize to the number of bytes of data in the entry the
    ** cursor currently points to.
    **
    ** The caller must guarantee that the cursor is pointing to a non-NULL
    ** valid entry.  In other words, the calling procedure must guarantee
    ** that the cursor has Cursor.eState==CursorFlag.CURSOR_VALID.
    **
    ** Failure is not possible.  This function always returns StatusCode.SQLITE_OK.
    ** It might just as well be a procedure (returning void) but we continue
    ** to return an integer result code for historical reasons.
    */

        public static int BtreeDataSize(BtCursor pCur, ref uint pSize)
        {
            Debug.Assert(cursorHoldsMutex(pCur));
            Debug.Assert(pCur.eState == CursorFlag.CURSOR_VALID);
            getCellInfo(pCur);
            pSize = pCur.info.nData;
            return StatusCode.SQLITE_OK;
        }

        /*
    ** Given the page number of an overflow page in the database (parameter
    ** ovfl), this function finds the page number of the next page in the
    ** linked list of overflow pages. If possible, it uses the auto-vacuum
    ** pointer-map data instead of reading the content of page ovfl to do so.
    **
    ** If an error occurs an SQLite error code is returned. Otherwise:
    **
    ** The page number of the next overflow page in the linked list is
    ** written to puintNext. If page ovfl is the last page in its linked
    ** list, puintNext is set to zero.
    **
    ** If ppPage is not NULL, and a reference to the MemPage object corresponding
    ** to page number pOvfl was obtained, then ppPage is set to point to that
    ** reference. It is the responsibility of the caller to call releasePage()
    ** on ppPage to free the reference. In no reference was obtained (because
    ** the pointer-map was used to obtain the value for puintNext), then
    ** ppPage is set to zero.
    */

        private static int getOverflowPage(
            BtShared pBt, /* The database file */
            uint ovfl, /* Current overflow page number */
            ref MemPage ppPage, /* OUT: MemPage handle (may be NULL) */
            ref uint puintNext /* OUT: Next overflow page number */
            )
        {
            uint next = 0;
            MemPage pPage = null;
            int rc = StatusCode.SQLITE_OK;

            Debug.Assert(MutexHelper.MutexHeld(pBt.mutex));
            // Debug.Assert( puintNext);

#if !SQLITE_OMIT_AUTOVACUUM
            /* Try to find the next page in the overflow list using the
** autovacuum pointer-map pages. Guess that the next page in
** the overflow list is page number (ovfl+1). If that guess turns
** out to be wrong, fall back to loading the data of page
** number ovfl to determine the next page number.
*/
            if (pBt.autoVacuum)
            {
                uint pgno = 0;
                uint iGuess = ovfl + 1;
                byte eType = 0;

                while (PTRMAP_ISPAGE(pBt, iGuess) || iGuess == Utility.PENDING_BYTE_PAGE(pBt))
                {
                    iGuess++;
                }

                if (iGuess <= pagerPagecount(pBt))
                {
                    rc = ptrmapGet(pBt, iGuess, ref eType, ref pgno);
                    if (rc == StatusCode.SQLITE_OK && eType == PTRMAP_OVERFLOW2 && pgno == ovfl)
                    {
                        next = iGuess;
                        rc = StatusCode.SQLITE_DONE;
                    }
                }
            }
#endif

            Debug.Assert(next == 0 || rc == StatusCode.SQLITE_DONE);
            if (rc == StatusCode.SQLITE_OK)
            {
                rc = btreeGetPage(pBt, ovfl, ref pPage, 0);
                Debug.Assert(rc == StatusCode.SQLITE_OK || pPage == null);
                if (rc == StatusCode.SQLITE_OK)
                {
                    next = Utility.Sqlite3Get4byte(pPage.aData);
                }
            }

            puintNext = next;
            if (ppPage != null)
            {
                ppPage = pPage;
            }
            else
            {
                releasePage(pPage);
            }
            return (rc == StatusCode.SQLITE_DONE ? StatusCode.SQLITE_OK : rc);
        }

        /*
    ** Copy data from a buffer to a page, or from a page to a buffer.
    **
    ** pPayload is a pointer to data stored on database page pDbPage.
    ** If argument eOp is false, then nByte bytes of data are copied
    ** from pPayload to the buffer pointed at by pBuf. If eOp is true,
    ** then PagerHelper.PagerWrite() is called on pDbPage and nByte bytes
    ** of data are copied from the buffer pBuf to pPayload.
    **
    ** StatusCode.SQLITE_OK is returned on success, otherwise an error code.
    */

        private static int copyPayload(
            byte[] pPayload, /* Pointer to page data */
            uint payloadOffset, /* Offset into page data */
            byte[] pBuf, /* Pointer to buffer */
            uint pBufOffset, /* Offset into buffer */
            uint nByte, /* Number of bytes to copy */
            int eOp, /* 0 . copy from page, 1 . copy to page */
            DbPage pDbPage /* Page containing pPayload */
            )
        {
            if (eOp != 0)
            {
                /* Copy data from buffer to page (a write operation) */
                int rc = PagerHelper.PagerWrite(pDbPage);
                if (rc != StatusCode.SQLITE_OK)
                {
                    return rc;
                }
                Buffer.BlockCopy(pBuf, (int) pBufOffset, pPayload, (int) payloadOffset, (int) nByte);
                    // memcpy( pPayload, pBuf, nByte );
            }
            else
            {
                /* Copy data from page to buffer (a read operation) */
                Buffer.BlockCopy(pPayload, (int) payloadOffset, pBuf, (int) pBufOffset, (int) nByte);
                    //memcpy(pBuf, pPayload, nByte);
            }
            return StatusCode.SQLITE_OK;
        }

        //static int copyPayload(
        //  byte[] pPayload,           /* Pointer to page data */
        //  byte[] pBuf,               /* Pointer to buffer */
        //  int nByte,                 /* Number of bytes to copy */
        //  int eOp,                   /* 0 -> copy from page, 1 -> copy to page */
        //  DbPage pDbPage             /* Page containing pPayload */
        //){
        //  if( eOp!=0 ){
        //    /* Copy data from buffer to page (a write operation) */
        //    int rc = PagerHelper.PagerWrite(pDbPage);
        //    if( rc!=StatusCode.SQLITE_OK ){
        //      return rc;
        //    }
        //    memcpy(pPayload, pBuf, nByte);
        //  }else{
        //    /* Copy data from page to buffer (a read operation) */
        //    memcpy(pBuf, pPayload, nByte);
        //  }
        //  return StatusCode.SQLITE_OK;
        //}

        /*
    ** This function is used to read or overwrite payload information
    ** for the entry that the pCur cursor is pointing to. If the eOp
    ** parameter is 0, this is a read operation (data copied into
    ** buffer pBuf). If it is non-zero, a write (data copied from
    ** buffer pBuf).
    **
    ** A total of "amt" bytes are read or written beginning at "offset".
    ** Data is read to or from the buffer pBuf.
    **
    ** The content being read or written might appear on the main page
    ** or be scattered out on multiple overflow pages.
    **
    ** If the BtCursor.isIncrblobHandle flag is set, and the current
    ** cursor entry uses one or more overflow pages, this function
    ** allocates space for and lazily popluates the overflow page-list
    ** cache array (BtCursor.aOverflow). Subsequent calls use this
    ** cache to make seeking to the supplied offset more efficient.
    **
    ** Once an overflow page-list cache has been allocated, it may be
    ** invalidated if some other cursor writes to the same table, or if
    ** the cursor is moved to a different row. Additionally, in auto-vacuum
    ** mode, the following events may invalidate an overflow page-list cache.
    **
    **   * An incremental vacuum,
    **   * A commit in auto_vacuum="full" mode,
    **   * Creating a table (may require moving an overflow page).
    */

        private static int accessPayload(
            BtCursor pCur, /* Cursor pointing to entry to read from */
            uint offset, /* Begin reading this far into payload */
            uint amt, /* Read this many bytes */
            byte[] pBuf, /* Write the bytes into this buffer */
            int eOp /* zero to read. non-zero to write. */
            )
        {
            uint pBufOffset = 0;
            byte[] aPayload;
            int rc = StatusCode.SQLITE_OK;
            uint nKey;
            int iIdx = 0;
            MemPage pPage = pCur.apPage[pCur.iPage]; /* Btree page of current entry */
            BtShared pBt = pCur.pBt; /* Btree this cursor belongs to */

            Debug.Assert(pPage != null);
            Debug.Assert(pCur.eState == CursorFlag.CURSOR_VALID);
            Debug.Assert(pCur.aiIdx[pCur.iPage] < pPage.nCell);
            Debug.Assert(cursorHoldsMutex(pCur));

            getCellInfo(pCur);
            aPayload = pCur.info.pCell; //pCur.info.pCell + pCur.info.nHeader;
            nKey = (uint) (pPage.intKey != 0 ? 0 : (int) pCur.info.nKey);

            if (UnitTest.NEVER(offset + amt > nKey + pCur.info.nData)
                || pCur.info.nLocal > pBt.usableSize //&aPayload[pCur.info.nLocal] > &pPage.aData[pBt.usableSize]
                )
            {
                /* Trying to read or write past the end of the data is an error */
                return UnitTest.SQLITE_CORRUPT_BKPT();
            }

            /* Check if data must be read/written to/from the btree page itself. */
            if (offset < pCur.info.nLocal)
            {
                var a = (int) amt;
                if (a + offset > pCur.info.nLocal)
                {
                    a = (int) (pCur.info.nLocal - offset);
                }
                rc = copyPayload(aPayload, (uint) (offset + pCur.info.iCell + pCur.info.nHeader), pBuf, pBufOffset,
                                 (uint) a, eOp, pPage.pDbPage);
                offset = 0;
                pBufOffset += (uint) a; //pBuf += a;
                amt -= (uint) a;
            }
            else
            {
                offset -= pCur.info.nLocal;
            }

            if (rc == StatusCode.SQLITE_OK && amt > 0)
            {
                var ovflSize = (uint) (pBt.usableSize - 4); /* Bytes content per ovfl page */
                uint nextPage;

                nextPage = Utility.Sqlite3Get4byte(aPayload, pCur.info.nLocal + pCur.info.iCell + pCur.info.nHeader);

#if !SQLITE_OMIT_INCRBLOB
/* If the isIncrblobHandle flag is set and the BtCursor.aOverflow[]
** has not been allocated, allocate it now. The array is sized at
** one entry for each overflow page in the overflow chain. The
** page number of the first overflow page is stored in aOverflow[0],
** etc. A value of 0 in the aOverflow[] array means "not yet known"
** (the cache is lazily populated).
*/
if( pCur.isIncrblobHandle && !pCur.aOverflow ){
int nOvfl = (pCur.info.nPayload-pCur.info.nLocal+ovflSize-1)/ovflSize;
pCur.aOverflow = (uint *)Malloc.MallocZero(sizeof(uint)*nOvfl);
/* nOvfl is always positive.  If it were zero, fetchPayload would have
** been used instead of this routine. */
if( UnitTest.ALWAYS(nOvfl) && !pCur.aOverflow ){
rc = StatusCode.SQLITE_NOMEM;
}
}

/* If the overflow page-list cache has been allocated and the
** entry for the first required overflow page is valid, skip
** directly to it.
*/
if( pCur.aOverflow && pCur.aOverflow[offset/ovflSize] ){
iIdx = (offset/ovflSize);
nextPage = pCur.aOverflow[iIdx];
offset = (offset%ovflSize);
}
#endif

                for (; rc == StatusCode.SQLITE_OK && amt > 0 && nextPage != 0; iIdx++)
                {
#if !SQLITE_OMIT_INCRBLOB
/* If required, populate the overflow page-list cache. */
if( pCur.aOverflow ){
Debug.Assert(!pCur.aOverflow[iIdx] || pCur.aOverflow[iIdx]==nextPage);
pCur.aOverflow[iIdx] = nextPage;
}
#endif

                    MemPage MemPageDummy = null;
                    if (offset >= ovflSize)
                    {
                        /* The only reason to read this page is to obtain the page
            ** number for the next page in the overflow chain. The page
            ** data is not required. So first try to lookup the overflow
            ** page-list cache, if any, then fall back to the getOverflowPage()
            ** function.
            */
#if !SQLITE_OMIT_INCRBLOB
if( pCur.aOverflow && pCur.aOverflow[iIdx+1] ){
nextPage = pCur.aOverflow[iIdx+1];
} else
#endif
                        rc = getOverflowPage(pBt, nextPage, ref MemPageDummy, ref nextPage);
                        offset -= ovflSize;
                    }
                    else
                    {
                        /* Need to read this page properly. It contains some of the
            ** range of data that is being read (eOp==null) or written (eOp!=null).
            */
                        var pDbPage = new PgHdr();
                        var a = (int) amt;
                        rc = sqlite3PagerGet(pBt.pPager, nextPage, ref pDbPage);
                        if (rc == StatusCode.SQLITE_OK)
                        {
                            aPayload = sqlite3PagerGetData(pDbPage);
                            nextPage = Utility.Sqlite3Get4byte(aPayload);
                            if (a + offset > ovflSize)
                            {
                                a = (int) (ovflSize - offset);
                            }
                            rc = copyPayload(aPayload, offset + 4, pBuf, pBufOffset, (uint) a, eOp, pDbPage);
                            sqlite3PagerUnref(pDbPage);
                            offset = 0;
                            amt -= (uint) a;
                            pBufOffset += (uint) a; //pBuf += a;
                        }
                    }
                }
            }

            if (rc == StatusCode.SQLITE_OK && amt > 0)
            {
                return UnitTest.SQLITE_CORRUPT_BKPT();
            }
            return rc;
        }

        /*
    ** Read part of the key associated with cursor pCur.  Exactly
    ** "amt" bytes will be transfered into pBuf[].  The transfer
    ** begins at "offset".
    **
    ** The caller must ensure that pCur is pointing to a valid row
    ** in the table.
    **
    ** Return StatusCode.SQLITE_OK on success or an error code if anything goes
    ** wrong.  An error is returned if "offset+amt" is larger than
    ** the available payload.
    */

        public static int BtreeKey(BtCursor pCur, uint offset, uint amt, byte[] pBuf)
        {
            Debug.Assert(cursorHoldsMutex(pCur));
            Debug.Assert(pCur.eState == CursorFlag.CURSOR_VALID);
            Debug.Assert(pCur.iPage >= 0 && pCur.apPage[pCur.iPage] != null);
            Debug.Assert(pCur.aiIdx[pCur.iPage] < pCur.apPage[pCur.iPage].nCell);
            return accessPayload(pCur, offset, amt, pBuf, 0);
        }

        /*
    ** Read part of the data associated with cursor pCur.  Exactly
    ** "amt" bytes will be transfered into pBuf[].  The transfer
    ** begins at "offset".
    **
    ** Return StatusCode.SQLITE_OK on success or an error code if anything goes
    ** wrong.  An error is returned if "offset+amt" is larger than
    ** the available payload.
    */

        public static int BtreeData(BtCursor pCur, uint offset, uint amt, byte[] pBuf)
        {
            int rc;

#if !SQLITE_OMIT_INCRBLOB
if ( pCur.eState==CursorFlag.CURSOR_INVALID ){
return StatusCode.SQLITE_ABORT;
}
#endif

            Debug.Assert(cursorHoldsMutex(pCur));
            rc = restoreCursorPosition(pCur);
            if (rc == StatusCode.SQLITE_OK)
            {
                Debug.Assert(pCur.eState == CursorFlag.CURSOR_VALID);
                Debug.Assert(pCur.iPage >= 0 && pCur.apPage[pCur.iPage] != null);
                Debug.Assert(pCur.aiIdx[pCur.iPage] < pCur.apPage[pCur.iPage].nCell);
                rc = accessPayload(pCur, offset, amt, pBuf, 0);
            }
            return rc;
        }

        /*
    ** Return a pointer to payload information from the entry that the
    ** pCur cursor is pointing to.  The pointer is to the beginning of
    ** the key if skipKey==null and it points to the beginning of data if
    ** skipKey==1.  The number of bytes of available key/data is written
    ** into pAmt.  If pAmt==null, then the value returned will not be
    ** a valid pointer.
    **
    ** This routine is an optimization.  It is common for the entire key
    ** and data to fit on the local page and for there to be no overflow
    ** pages.  When that is so, this routine can be used to access the
    ** key and data without making a copy.  If the key and/or data spills
    ** onto overflow pages, then accessPayload() must be used to reassemble
    ** the key/data and copy it into a preallocated buffer.
    **
    ** The pointer returned by this routine looks directly into the cached
    ** page of the database.  The data might change or move the next time
    ** any btree routine is called.
    */

        private static byte[] fetchPayload(
            BtCursor pCur, /* Cursor pointing to entry to read from */
            ref int pAmt, /* Write the number of available bytes here */
            ref int outOffset, /* Offset into Buffer */
            bool skipKey /* read beginning at data if this is true */
            )
        {
            byte[] aPayload;
            MemPage pPage;
            uint nKey;
            uint nLocal;

            Debug.Assert(pCur != null && pCur.iPage >= 0 && pCur.apPage[pCur.iPage] != null);
            Debug.Assert(pCur.eState == CursorFlag.CURSOR_VALID);
            Debug.Assert(cursorHoldsMutex(pCur));
            outOffset = -1;
            pPage = pCur.apPage[pCur.iPage];
            Debug.Assert(pCur.aiIdx[pCur.iPage] < pPage.nCell);
            if (UnitTest.NEVER(pCur.info.nSize == 0))
            {
                btreeParseCell(pCur.apPage[pCur.iPage], pCur.aiIdx[pCur.iPage],
                               ref pCur.info);
            }
            //aPayload = pCur.info.pCell;
            //aPayload += pCur.info.nHeader;
            aPayload = Malloc.sqlite3Malloc(pCur.info.nSize - pCur.info.nHeader);
            if (pPage.intKey != 0)
            {
                nKey = 0;
            }
            else
            {
                nKey = (uint) pCur.info.nKey;
            }
            if (skipKey)
            {
                //aPayload += nKey;
                outOffset = (int) (pCur.info.iCell + pCur.info.nHeader + nKey);
                Buffer.BlockCopy(pCur.info.pCell, outOffset, aPayload, 0,
                                 (int) (pCur.info.nSize - pCur.info.nHeader - nKey));
                nLocal = pCur.info.nLocal - nKey;
            }
            else
            {
                outOffset = (pCur.info.iCell + pCur.info.nHeader);
                Buffer.BlockCopy(pCur.info.pCell, outOffset, aPayload, 0, pCur.info.nSize - pCur.info.nHeader);
                nLocal = pCur.info.nLocal;
                Debug.Assert(nLocal <= nKey);
            }
            pAmt = (int) nLocal;
            return aPayload;
        }

        /*
    ** For the entry that cursor pCur is point to, return as
    ** many bytes of the key or data as are available on the local
    ** b-tree page.  Write the number of available bytes into pAmt.
    **
    ** The pointer returned is ephemeral.  The key/data may move
    ** or be destroyed on the next call to any Btree routine,
    ** including calls from other threads against the same cache.
    ** Hence, a mutex on the BtShared should be held prior to calling
    ** this routine.
    **
    ** These routines is used to get quick access to key and data
    ** in the common case where no overflow pages are used.
    */

        public static byte[] BtreeKeyFetch(BtCursor pCur, ref int pAmt, ref int outOffset)
        {
            byte[] p = null;
            Debug.Assert(MutexHelper.MutexHeld(pCur.pBtree.db.mutex));
            Debug.Assert(cursorHoldsMutex(pCur));
            if (UnitTest.ALWAYS(pCur.eState == CursorFlag.CURSOR_VALID))
            {
                p = fetchPayload(pCur, ref pAmt, ref outOffset, false);
            }
            return p;
        }

        public static byte[] BtreeDataFetch(BtCursor pCur, ref int pAmt, ref int outOffset)
        {
            byte[] p = null;
            Debug.Assert(MutexHelper.MutexHeld(pCur.pBtree.db.mutex));
            Debug.Assert(cursorHoldsMutex(pCur));
            if (UnitTest.ALWAYS(pCur.eState == CursorFlag.CURSOR_VALID))
            {
                p = fetchPayload(pCur, ref pAmt, ref outOffset, true);
            }
            return p;
        }

        /*
    ** Move the cursor down to a new child page.  The newuint argument is the
    ** page number of the child page to move to.
    **
    ** This function returns StatusCode.SQLITE_CORRUPT if the page-header flags field of
    ** the new child page does not match the flags field of the parent (i.e.
    ** if an intkey page appears to be the parent of a non-intkey page, or
    ** vice-versa).
    */

        private static int moveToChild(BtCursor pCur, uint newuint)
        {
            int rc;
            int i = pCur.iPage;
            var pNewPage = new MemPage();
            BtShared pBt = pCur.pBt;

            Debug.Assert(cursorHoldsMutex(pCur));
            Debug.Assert(pCur.eState == CursorFlag.CURSOR_VALID);
            Debug.Assert(pCur.iPage < Const.BTCURSOR_MAX_DEPTH);
            if (pCur.iPage >= (Const.BTCURSOR_MAX_DEPTH - 1))
            {
                return UnitTest.SQLITE_CORRUPT_BKPT();
            }
            rc = getAndInitPage(pBt, newuint, ref pNewPage);
            if (rc != 0) return rc;
            pCur.apPage[i + 1] = pNewPage;
            pCur.aiIdx[i + 1] = 0;
            pCur.iPage++;

            pCur.info.nSize = 0;
            pCur.validNKey = false;
            if (pNewPage.nCell < 1 || pNewPage.intKey != pCur.apPage[i].intKey)
            {
                return UnitTest.SQLITE_CORRUPT_BKPT();
            }
            return StatusCode.SQLITE_OK;
        }

#if !NDEBUG
    /*
** Page pParent is an internal (non-leaf) tree page. This function
** asserts that page number iChild is the left-child if the iIdx'th
** cell in page pParent. Or, if iIdx is equal to the total number of
** cells in pParent, that page number iChild is the right-child of
** the page.
*/
    static void assertParentIndex( MemPage pParent, int iIdx, uint iChild )
    {
      Debug.Assert( iIdx <= pParent.nCell );
      if ( iIdx == pParent.nCell )
      {
        Debug.Assert( Utility.Sqlite3Get4byte( pParent.aData, pParent.hdrOffset + 8 ) == iChild );
      }
      else
      {
        Debug.Assert( Utility.Sqlite3Get4byte( pParent.aData, findCell( pParent, iIdx ) ) == iChild );
      }
    }
#else
//#  define assertParentIndex(x,y,z)
        private static void assertParentIndex(MemPage pParent, int iIdx, uint iChild)
        {
        }
#endif

        /*
** Move the cursor up to the parent page.
**
** pCur.idx is set to the cell index that contains the pointer
** to the page we are coming from.  If we are coming from the
** right-most child page then pCur.idx is set to one more than
** the largest cell index.
*/

        private static void moveToParent(BtCursor pCur)
        {
            Debug.Assert(cursorHoldsMutex(pCur));
            Debug.Assert(pCur.eState == CursorFlag.CURSOR_VALID);
            Debug.Assert(pCur.iPage > 0);
            Debug.Assert(pCur.apPage[pCur.iPage] != null);
            assertParentIndex(
                pCur.apPage[pCur.iPage - 1],
                pCur.aiIdx[pCur.iPage - 1],
                pCur.apPage[pCur.iPage].pgno
                );
            releasePage(pCur.apPage[pCur.iPage]);
            pCur.iPage--;
            pCur.info.nSize = 0;
            pCur.validNKey = false;
        }

        /*
    ** Move the cursor to point to the root page of its b-tree structure.
    **
    ** If the table has a virtual root page, then the cursor is moved to point
    ** to the virtual root page instead of the actual root page. A table has a
    ** virtual root page when the actual root page contains no cells and a
    ** single child page. This can only happen with the table rooted at page 1.
    **
    ** If the b-tree structure is empty, the cursor state is set to
    ** CursorFlag.CURSOR_INVALID. Otherwise, the cursor is set to point to the first
    ** cell located on the root (or virtual root) page and the cursor state
    ** is set to CursorFlag.CURSOR_VALID.
    **
    ** If this function returns successfully, it may be assumed that the
    ** page-header flags indicate that the [virtual] root-page is the expected
    ** kind of b-tree page (i.e. if when opening the cursor the caller did not
    ** specify a KeyInfo structure the flags byte is set to 0x05 or 0x0D,
    ** indicating a table b-tree, or if the caller did specify a KeyInfo
    ** structure the flags byte is set to 0x02 or 0x0A, indicating an index
    ** b-tree).
    */

        private static int moveToRoot(BtCursor pCur)
        {
            MemPage pRoot;
            int rc = StatusCode.SQLITE_OK;
            Btree p = pCur.pBtree;
            BtShared pBt = p.pBt;

            Debug.Assert(cursorHoldsMutex(pCur));
            Debug.Assert(CursorFlag.CURSOR_INVALID < CursorFlag.CURSOR_REQUIRESEEK);
            Debug.Assert(CursorFlag.CURSOR_VALID < CursorFlag.CURSOR_REQUIRESEEK);
            Debug.Assert(CursorFlag.CURSOR_FAULT > CursorFlag.CURSOR_REQUIRESEEK);
            if (pCur.eState >= CursorFlag.CURSOR_REQUIRESEEK)
            {
                if (pCur.eState == CursorFlag.CURSOR_FAULT)
                {
                    Debug.Assert(pCur.skipNext != StatusCode.SQLITE_OK);
                    return pCur.skipNext;
                }
                sqlite3BtreeClearCursor(pCur);
            }

            if (pCur.iPage >= 0)
            {
                int i;
                for (i = 1; i <= pCur.iPage; i++)
                {
                    releasePage(pCur.apPage[i]);
                }
                pCur.iPage = 0;
            }
            else
            {
                rc = getAndInitPage(pBt, pCur.pgnoRoot, ref pCur.apPage[0]);
                if (rc != StatusCode.SQLITE_OK)
                {
                    pCur.eState = CursorFlag.CURSOR_INVALID;
                    return rc;
                }
                pCur.iPage = 0;

                /* If pCur.pKeyInfo is not NULL, then the caller that opened this cursor
        ** expected to open it on an index b-tree. Otherwise, if pKeyInfo is
        ** NULL, the caller expects a table b-tree. If this is not the case,
        ** return an StatusCode.SQLITE_CORRUPT error.  */
                Debug.Assert(pCur.apPage[0].intKey == 1 || pCur.apPage[0].intKey == 0);
                if ((pCur.pKeyInfo == null) != (pCur.apPage[0].intKey != 0))
                {
                    return UnitTest.SQLITE_CORRUPT_BKPT();
                }
            }

            /* Assert that the root page is of the correct type. This must be the
      ** case as the call to this function that loaded the root-page (either
      ** this call or a previous invocation) would have detected corruption
      ** if the assumption were not true, and it is not possible for the flags
      ** byte to have been modified while this cursor is holding a reference
      ** to the page.  */
            pRoot = pCur.apPage[0];
            Debug.Assert(pRoot.pgno == pCur.pgnoRoot);
            Debug.Assert(pRoot.isInit != 0 && (pCur.pKeyInfo == null) == (pRoot.intKey != 0));

            pCur.aiIdx[0] = 0;
            pCur.info.nSize = 0;
            pCur.atLast = 0;
            pCur.validNKey = false;

            if (pRoot.nCell == 0 && 0 == pRoot.leaf)
            {
                uint subpage;
                if (pRoot.pgno != 1)
                    return UnitTest.SQLITE_CORRUPT_BKPT();
                subpage = Utility.Sqlite3Get4byte(pRoot.aData, pRoot.hdrOffset + 8);
                pCur.eState = CursorFlag.CURSOR_VALID;
                rc = moveToChild(pCur, subpage);
            }
            else
            {
                pCur.eState = ((pRoot.nCell > 0) ? CursorFlag.CURSOR_VALID : CursorFlag.CURSOR_INVALID);
            }
            return rc;
        }

        /*
    ** Move the cursor down to the left-most leaf entry beneath the
    ** entry to which it is currently pointing.
    **
    ** The left-most leaf is the one with the smallest key - the first
    ** in ascending order.
    */

        private static int moveToLeftmost(BtCursor pCur)
        {
            uint pgno;
            int rc = StatusCode.SQLITE_OK;
            MemPage pPage;

            Debug.Assert(cursorHoldsMutex(pCur));
            Debug.Assert(pCur.eState == CursorFlag.CURSOR_VALID);
            while (rc == StatusCode.SQLITE_OK && 0 == (pPage = pCur.apPage[pCur.iPage]).leaf)
            {
                Debug.Assert(pCur.aiIdx[pCur.iPage] < pPage.nCell);
                pgno = Utility.Sqlite3Get4byte(pPage.aData, findCell(pPage, pCur.aiIdx[pCur.iPage]));
                rc = moveToChild(pCur, pgno);
            }
            return rc;
        }

        /*
    ** Move the cursor down to the right-most leaf entry beneath the
    ** page to which it is currently pointing.  Notice the difference
    ** between moveToLeftmost() and moveToRightmost().  moveToLeftmost()
    ** finds the left-most entry beneath the *entry* whereas moveToRightmost()
    ** finds the right-most entry beneath the page*.
    **
    ** The right-most entry is the one with the largest key - the last
    ** key in ascending order.
    */

        private static int moveToRightmost(BtCursor pCur)
        {
            uint pgno;
            int rc = StatusCode.SQLITE_OK;
            MemPage pPage = null;

            Debug.Assert(cursorHoldsMutex(pCur));
            Debug.Assert(pCur.eState == CursorFlag.CURSOR_VALID);
            while (rc == StatusCode.SQLITE_OK && 0 == (pPage = pCur.apPage[pCur.iPage]).leaf)
            {
                pgno = Utility.Sqlite3Get4byte(pPage.aData, pPage.hdrOffset + 8);
                pCur.aiIdx[pCur.iPage] = pPage.nCell;
                rc = moveToChild(pCur, pgno);
            }
            if (rc == StatusCode.SQLITE_OK)
            {
                pCur.aiIdx[pCur.iPage] = (ushort) (pPage.nCell - 1);
                pCur.info.nSize = 0;
                pCur.validNKey = false;
            }
            return rc;
        }

        /* Move the cursor to the first entry in the table.  Return StatusCode.SQLITE_OK
    ** on success.  Set pRes to 0 if the cursor actually points to something
    ** or set pRes to 1 if the table is empty.
    */

        public static int BtreeFirst(BtCursor pCur, ref int pRes)
        {
            int rc;

            Debug.Assert(cursorHoldsMutex(pCur));
            Debug.Assert(MutexHelper.MutexHeld(pCur.pBtree.db.mutex));
            rc = moveToRoot(pCur);
            if (rc == StatusCode.SQLITE_OK)
            {
                if (pCur.eState == CursorFlag.CURSOR_INVALID)
                {
                    Debug.Assert(pCur.apPage[pCur.iPage].nCell == 0);
                    pRes = 1;
                    rc = StatusCode.SQLITE_OK;
                }
                else
                {
                    Debug.Assert(pCur.apPage[pCur.iPage].nCell > 0);
                    pRes = 0;
                    rc = moveToLeftmost(pCur);
                }
            }
            return rc;
        }

        /* Move the cursor to the last entry in the table.  Return StatusCode.SQLITE_OK
    ** on success.  Set pRes to 0 if the cursor actually points to something
    ** or set pRes to 1 if the table is empty.
    */

        public static int BtreeLast(BtCursor pCur, ref int pRes)
        {
            int rc;

            Debug.Assert(cursorHoldsMutex(pCur));
            Debug.Assert(MutexHelper.MutexHeld(pCur.pBtree.db.mutex));

            /* If the cursor already points to the last entry, this is a no-op. */
            if (CursorFlag.CURSOR_VALID == pCur.eState && pCur.atLast != 0)
            {
#if SQLITE_DEBUG
                /* This block serves to Debug.Assert() that the cursor really does point
** to the last entry in the b-tree. */
                int ii;
                for (ii = 0; ii < pCur.iPage; ii++)
                {
                    Debug.Assert(pCur.aiIdx[ii] == pCur.apPage[ii].nCell);
                }
                Debug.Assert(pCur.aiIdx[pCur.iPage] == pCur.apPage[pCur.iPage].nCell - 1);
                Debug.Assert(pCur.apPage[pCur.iPage].leaf != 0);
#endif
                return StatusCode.SQLITE_OK;
            }

            rc = moveToRoot(pCur);
            if (rc == StatusCode.SQLITE_OK)
            {
                if (CursorFlag.CURSOR_INVALID == pCur.eState)
                {
                    Debug.Assert(pCur.apPage[pCur.iPage].nCell == 0);
                    pRes = 1;
                }
                else
                {
                    Debug.Assert(pCur.eState == CursorFlag.CURSOR_VALID);
                    pRes = 0;
                    rc = moveToRightmost(pCur);
                    pCur.atLast = (byte) (rc == StatusCode.SQLITE_OK ? 1 : 0);
                }
            }
            return rc;
        }

        /* Move the cursor so that it points to an entry near the key
    ** specified by pIdxKey or intKey.   Return a success code.
    **
    ** For INTKEY tables, the intKey parameter is used.  pIdxKey
    ** must be NULL.  For index tables, pIdxKey is used and intKey
    ** is ignored.
    **
    ** If an exact match is not found, then the cursor is always
    ** left pointing at a leaf page which would hold the entry if it
    ** were present.  The cursor might point to an entry that comes
    ** before or after the key.
    **
    ** An integer is written into pRes which is the result of
    ** comparing the key with the entry to which the cursor is
    ** pointing.  The meaning of the integer written into
    ** pRes is as follows:
    **
    **     pRes<0      The cursor is left pointing at an entry that
    **                  is smaller than intKey/pIdxKey or if the table is empty
    **                  and the cursor is therefore left point to nothing.
    **
    **     pRes==null     The cursor is left pointing at an entry that
    **                  exactly matches intKey/pIdxKey.
    **
    **     pRes>0      The cursor is left pointing at an entry that
    **                  is larger than intKey/pIdxKey.
    **
    */

        public static int BtreeMovetoUnpacked(
            BtCursor pCur, /* The cursor to be moved */
            UnpackedRecord pIdxKey, /* Unpacked index key */
            long intKey, /* The table key */
            int biasRight, /* If true, bias the search to the high end */
            ref int pRes /* Write search results here */
            )
        {
            int rc;

            Debug.Assert(cursorHoldsMutex(pCur));
            Debug.Assert(MutexHelper.MutexHeld(pCur.pBtree.db.mutex));
            // Not needed in C# // Debug.Assert( pRes != 0 );
            Debug.Assert((pIdxKey == null) == (pCur.pKeyInfo == null));

            /* If the cursor is already positioned at the point we are trying
      ** to move to, then just return without doing any work */
            if (pCur.eState == CursorFlag.CURSOR_VALID && pCur.validNKey
                && pCur.apPage[0].intKey != 0
                )
            {
                if (pCur.info.nKey == intKey)
                {
                    pRes = 0;
                    return StatusCode.SQLITE_OK;
                }
                if (pCur.atLast != 0 && pCur.info.nKey < intKey)
                {
                    pRes = -1;
                    return StatusCode.SQLITE_OK;
                }
            }

            rc = moveToRoot(pCur);
            if (rc != 0)
            {
                return rc;
            }
            Debug.Assert(pCur.apPage[pCur.iPage] != null);
            Debug.Assert(pCur.apPage[pCur.iPage].isInit != 0);
            Debug.Assert(pCur.apPage[pCur.iPage].nCell > 0 || pCur.eState == CursorFlag.CURSOR_INVALID);
            if (pCur.eState == CursorFlag.CURSOR_INVALID)
            {
                pRes = -1;
                Debug.Assert(pCur.apPage[pCur.iPage].nCell == 0);
                return StatusCode.SQLITE_OK;
            }
            Debug.Assert(pCur.apPage[0].intKey != 0 || pIdxKey != null);
            for (;;)
            {
                int lwr, upr;
                uint chldPg;
                MemPage pPage = pCur.apPage[pCur.iPage];
                int c;

                /* pPage.nCell must be greater than zero. If this is the root-page
        ** the cursor would have been INVALID above and this for(;;) loop
        ** not run. If this is not the root-page, then the moveToChild() routine
        ** would have already detected db corruption. Similarly, pPage must
        ** be the right kind (index or table) of b-tree page. Otherwise
        ** a moveToChild() or moveToRoot() call would have detected corruption.  */
                Debug.Assert(pPage.nCell > 0);
                Debug.Assert(pPage.intKey == ((pIdxKey == null) ? 1 : 0));
                lwr = 0;
                upr = pPage.nCell - 1;
                if (biasRight != 0)
                {
                    pCur.aiIdx[pCur.iPage] = (ushort) upr;
                }
                else
                {
                    pCur.aiIdx[pCur.iPage] = (ushort) ((upr + lwr)/2);
                }
                for (;;)
                {
                    int idx = pCur.aiIdx[pCur.iPage]; /* Index of current cell in pPage */
                    int pCell; /* Pointer to current cell in pPage */

                    pCur.info.nSize = 0;
                    pCell = findCell(pPage, idx) + pPage.childPtrSize;
                    if (pPage.intKey != 0)
                    {
                        long nCellKey = 0;
                        if (pPage.hasData != 0)
                        {
                            uint Dummy0 = 0;
                            pCell += Utility.GetVarint32(pPage.aData, pCell, ref Dummy0);
                        }
                        Utility. GetVarint(pPage.aData, pCell, ref nCellKey);
                        if (nCellKey == intKey)
                        {
                            c = 0;
                        }
                        else if (nCellKey < intKey)
                        {
                            c = -1;
                        }
                        else
                        {
                            Debug.Assert(nCellKey > intKey);
                            c = +1;
                        }
                        pCur.validNKey = true;
                        pCur.info.nKey = nCellKey;
                    }
                    else
                    {
                        /* The maximum supported page-size is 32768 bytes. This means that
            ** the maximum number of record bytes stored on an index B-Tree
            ** page is at most 8198 bytes, which may be stored as a 2-byte
            ** varint. This information is used to attempt to avoid parsing
            ** the entire cell by checking for the cases where the record is
            ** stored entirely within the b-tree page by inspecting the first
            ** 2 bytes of the cell.
            */
                        int nCell = pPage.aData[pCell + 0]; //pCell[0];
                        if (0 == (nCell & 0x80) && nCell <= pPage.maxLocal)
                        {
                            /* This branch runs if the record-size field of the cell is a
              ** single byte varint and the record fits entirely on the main
              ** b-tree page.  */
                            c = VdbeAux.VdbeRecordCompare(nCell, pPage.aData, pCell + 1, pIdxKey);
                                //c = VdbeAux.VdbeRecordCompare( nCell, (void*)&pCell[1], pIdxKey );
                        }
                        else if (0 == (pPage.aData[pCell + 1] & 0x80) //!(pCell[1] & 0x80)
                                 && (nCell = ((nCell & 0x7f) << 7) + pPage.aData[pCell + 1]) <= pPage.maxLocal
                            //pCell[1])<=pPage.maxLocal
                            )
                        {
                            /* The record-size field is a 2 byte varint and the record
              ** fits entirely on the main b-tree page.  */
                            c = VdbeAux.VdbeRecordCompare(nCell, pPage.aData, pCell + 2, pIdxKey);
                                //c = VdbeAux.VdbeRecordCompare( nCell, (void*)&pCell[2], pIdxKey );
                        }
                        else
                        {
                            /* The record flows over onto one or more overflow pages. In
              ** this case the whole cell needs to be parsed, a buffer allocated
              ** and accessPayload() used to retrieve the record into the
              ** buffer before VdbeRecordCompare() can be called. */
                            byte[] pCellKey;
                            var pCellBody = new byte[pPage.aData.Length - pCell + pPage.childPtrSize];
                            Buffer.BlockCopy(pPage.aData, pCell - pPage.childPtrSize, pCellBody, 0, pCellBody.Length);
                                //          byte * const pCellBody = pCell - pPage->childPtrSize;
                            btreeParseCellPtr(pPage, pCellBody, ref pCur.info);
                            nCell = (int) pCur.info.nKey;
                            pCellKey = Malloc.sqlite3Malloc(nCell);
                            //if ( pCellKey == null )
                            //{
                            //  rc = StatusCode.SQLITE_NOMEM;
                            //  goto moveto_finish;
                            //}
                            rc = accessPayload(pCur, 0, (uint) nCell, pCellKey, 0);
                            if (rc != 0)
                            {
                                pCellKey = null; // Malloc.sqlite3_free(ref pCellKey );
                                goto moveto_finish;
                            }
                            c = VdbeAux.VdbeRecordCompare(nCell, pCellKey, pIdxKey);
                            pCellKey = null; // Malloc.sqlite3_free(ref pCellKey );
                        }
                    }
                    if (c == 0)
                    {
                        if (pPage.intKey != 0 && 0 == pPage.leaf)
                        {
                            lwr = idx;
                            upr = lwr - 1;
                            break;
                        }
                        else
                        {
                            pRes = 0;
                            rc = StatusCode.SQLITE_OK;
                            goto moveto_finish;
                        }
                    }
                    if (c < 0)
                    {
                        lwr = idx + 1;
                    }
                    else
                    {
                        upr = idx - 1;
                    }
                    if (lwr > upr)
                    {
                        break;
                    }
                    pCur.aiIdx[pCur.iPage] = (ushort) ((lwr + upr)/2);
                }
                Debug.Assert(lwr == upr + 1);
                Debug.Assert(pPage.isInit != 0);
                if (pPage.leaf != 0)
                {
                    chldPg = 0;
                }
                else if (lwr >= pPage.nCell)
                {
                    chldPg = Utility.Sqlite3Get4byte(pPage.aData, pPage.hdrOffset + 8);
                }
                else
                {
                    chldPg = Utility.Sqlite3Get4byte(pPage.aData, findCell(pPage, lwr));
                }
                if (chldPg == 0)
                {
                    Debug.Assert(pCur.aiIdx[pCur.iPage] < pCur.apPage[pCur.iPage].nCell);
                    pRes = c;
                    rc = StatusCode.SQLITE_OK;
                    goto moveto_finish;
                }
                pCur.aiIdx[pCur.iPage] = (ushort) lwr;
                pCur.info.nSize = 0;
                pCur.validNKey = false;
                rc = moveToChild(pCur, chldPg);
                if (rc != 0) goto moveto_finish;
            }
            moveto_finish:
            return rc;
        }


        /*
    ** Return TRUE if the cursor is not pointing at an entry of the table.
    **
    ** TRUE will be returned after a call to BTreeHelper.BtreeNext() moves
    ** past the last entry in the table or sqlite3BtreePrev() moves past
    ** the first entry.  TRUE is also returned if the table is empty.
    */

        public static bool BtreeEof(BtCursor pCur)
        {
            /* TODO: What if the cursor is in CursorFlag.CURSOR_REQUIRESEEK but all table entries
      ** have been deleted? This API will need to change to return an error code
      ** as well as the boolean result value.
      */
            return (CursorFlag.CURSOR_VALID != pCur.eState);
        }

        /*
    ** Advance the cursor to the next entry in the database.  If
    ** successful then set pRes=0.  If the cursor
    ** was already pointing to the last entry in the database before
    ** this routine was called, then set pRes=1.
    */

        public static int BtreeNext(BtCursor pCur, ref int pRes)
        {
            int rc;
            int idx;
            MemPage pPage;

            Debug.Assert(cursorHoldsMutex(pCur));
            rc = restoreCursorPosition(pCur);
            if (rc != StatusCode.SQLITE_OK)
            {
                return rc;
            }
            // Not needed in C# // Debug.Assert( pRes != 0 );
            if (CursorFlag.CURSOR_INVALID == pCur.eState)
            {
                pRes = 1;
                return StatusCode.SQLITE_OK;
            }
            if (pCur.skipNext > 0)
            {
                pCur.skipNext = 0;
                pRes = 0;
                return StatusCode.SQLITE_OK;
            }
            pCur.skipNext = 0;

            pPage = pCur.apPage[pCur.iPage];
            idx = ++pCur.aiIdx[pCur.iPage];
            Debug.Assert(pPage.isInit != 0);
            Debug.Assert(idx <= pPage.nCell);

            pCur.info.nSize = 0;
            pCur.validNKey = false;
            if (idx >= pPage.nCell)
            {
                if (0 == pPage.leaf)
                {
                    rc = moveToChild(pCur, Utility.Sqlite3Get4byte(pPage.aData, pPage.hdrOffset + 8));
                    if (rc != 0) return rc;
                    rc = moveToLeftmost(pCur);
                    pRes = 0;
                    return rc;
                }
                do
                {
                    if (pCur.iPage == 0)
                    {
                        pRes = 1;
                        pCur.eState = CursorFlag.CURSOR_INVALID;
                        return StatusCode.SQLITE_OK;
                    }
                    moveToParent(pCur);
                    pPage = pCur.apPage[pCur.iPage];
                } while (pCur.aiIdx[pCur.iPage] >= pPage.nCell);
                pRes = 0;
                if (pPage.intKey != 0)
                {
                    rc = BtreeNext(pCur, ref pRes);
                }
                else
                {
                    rc = StatusCode.SQLITE_OK;
                }
                return rc;
            }
            pRes = 0;
            if (pPage.leaf != 0)
            {
                return StatusCode.SQLITE_OK;
            }
            rc = moveToLeftmost(pCur);
            return rc;
        }


        /*
    ** Step the cursor to the back to the previous entry in the database.  If
    ** successful then set pRes=0.  If the cursor
    ** was already pointing to the first entry in the database before
    ** this routine was called, then set pRes=1.
    */

        public static int BtreePrevious(BtCursor pCur, ref int pRes)
        {
            int rc;
            MemPage pPage;

            Debug.Assert(cursorHoldsMutex(pCur));
            rc = restoreCursorPosition(pCur);
            if (rc != StatusCode.SQLITE_OK)
            {
                return rc;
            }
            pCur.atLast = 0;
            if (CursorFlag.CURSOR_INVALID == pCur.eState)
            {
                pRes = 1;
                return StatusCode.SQLITE_OK;
            }
            if (pCur.skipNext < 0)
            {
                pCur.skipNext = 0;
                pRes = 0;
                return StatusCode.SQLITE_OK;
            }
            pCur.skipNext = 0;

            pPage = pCur.apPage[pCur.iPage];
            Debug.Assert(pPage.isInit != 0);
            if (0 == pPage.leaf)
            {
                int idx = pCur.aiIdx[pCur.iPage];
                rc = moveToChild(pCur, Utility.Sqlite3Get4byte(pPage.aData, findCell(pPage, idx)));
                if (rc != 0)
                {
                    return rc;
                }
                rc = moveToRightmost(pCur);
            }
            else
            {
                while (pCur.aiIdx[pCur.iPage] == 0)
                {
                    if (pCur.iPage == 0)
                    {
                        pCur.eState = CursorFlag.CURSOR_INVALID;
                        pRes = 1;
                        return StatusCode.SQLITE_OK;
                    }
                    moveToParent(pCur);
                }
                pCur.info.nSize = 0;
                pCur.validNKey = false;

                pCur.aiIdx[pCur.iPage]--;
                pPage = pCur.apPage[pCur.iPage];
                if (pPage.intKey != 0 && 0 == pPage.leaf)
                {
                    rc = BtreePrevious(pCur, ref pRes);
                }
                else
                {
                    rc = StatusCode.SQLITE_OK;
                }
            }
            pRes = 0;
            return rc;
        }

        /*
    ** Allocate a new page from the database file.
    **
    ** The new page is marked as dirty.  (In other words, PagerHelper.PagerWrite()
    ** has already been called on the new page.)  The new page has also
    ** been referenced and the calling routine is responsible for calling
    ** sqlite3PagerUnref() on the new page when it is done.
    **
    ** StatusCode.SQLITE_OK is returned on success.  Any other return value indicates
    ** an error.  ppPage and puint are undefined in the event of an error.
    ** Do not invoke sqlite3PagerUnref() on ppPage if an error is returned.
    **
    ** If the "nearby" parameter is not 0, then a (feeble) effort is made to
    ** locate a page close to the page number "nearby".  This can be used in an
    ** attempt to keep related pages close to each other in the database file,
    ** which in turn can make database access faster.
    **
    ** If the "exact" parameter is not 0, and the page-number nearby exists
    ** anywhere on the free-list, then it is guarenteed to be returned. This
    ** is only used by auto-vacuum databases when allocating a new table.
    */

        private static int allocateBtreePage(
            BtShared pBt,
            ref MemPage ppPage,
            ref uint puint,
            uint nearby,
            byte exact
            )
        {
            MemPage pPage1;
            int rc;
            uint n; /* Number of pages on the freelist */
            uint k; /* Number of leaves on the trunk of the freelist */
            MemPage pTrunk = null;
            MemPage pPrevTrunk = null;
            uint mxPage; /* Total size of the database file */

            Debug.Assert(MutexHelper.MutexHeld(pBt.mutex));
            pPage1 = pBt.pPage1;
            mxPage = pagerPagecount(pBt);
            n = Utility.Sqlite3Get4byte(pPage1.aData, 36);
            UnitTest.TestCase(n == mxPage - 1);
            if (n >= mxPage)
            {
                return UnitTest.SQLITE_CORRUPT_BKPT();
            }
            if (n > 0)
            {
                /* There are pages on the freelist.  Reuse one of those pages. */
                uint iTrunk;
                byte searchList = 0; /* If the free-list must be searched for 'nearby' */

                /* If the 'exact' parameter was true and a query of the pointer-map
        ** shows that the page 'nearby' is somewhere on the free-list, then
        ** the entire-list will be searched for that page.
        */
#if !SQLITE_OMIT_AUTOVACUUM
                if (exact != 0 && nearby <= mxPage)
                {
                    byte eType = 0;
                    Debug.Assert(nearby > 0);
                    Debug.Assert(pBt.autoVacuum);
                    uint Dummy0 = 0;
                    rc = ptrmapGet(pBt, nearby, ref eType, ref Dummy0);
                    if (rc != 0) return rc;
                    if (eType == PTRMAP_FREEPAGE)
                    {
                        searchList = 1;
                    }
                    puint = nearby;
                }
#endif

                /* Decrement the free-list count by 1. Set iTrunk to the index of the
** first free-list trunk page. iPrevTrunk is initially 1.
*/
                rc = PagerHelper.PagerWrite(pPage1.pDbPage);
                if (rc != 0) return rc;
                Utility.Sqlite3Put4byte(pPage1.aData, (uint) 36, n - 1);

                /* The code within this loop is run only once if the 'searchList' variable
        ** is not true. Otherwise, it runs once for each trunk-page on the
        ** free-list until the page 'nearby' is located.
        */
                do
                {
                    pPrevTrunk = pTrunk;
                    if (pPrevTrunk != null)
                    {
                        iTrunk = Utility.Sqlite3Get4byte(pPrevTrunk.aData, 0);
                    }
                    else
                    {
                        iTrunk = Utility.Sqlite3Get4byte(pPage1.aData, 32);
                    }
                    UnitTest.TestCase(iTrunk == mxPage);
                    if (iTrunk > mxPage)
                    {
                        rc = UnitTest.SQLITE_CORRUPT_BKPT();
                    }
                    else
                    {
                        rc = btreeGetPage(pBt, iTrunk, ref pTrunk, 0);
                    }
                    if (rc != 0)
                    {
                        pTrunk = null;
                        goto end_allocate_page;
                    }

                    k = Utility.Sqlite3Get4byte(pTrunk.aData, 4);
                    if (k == 0 && 0 == searchList)
                    {
                        /* The trunk has no leaves and the list is not being searched.
            ** So extract the trunk page itself and use it as the newly
            ** allocated page */
                        Debug.Assert(pPrevTrunk == null);
                        rc = PagerHelper.PagerWrite(pTrunk.pDbPage);
                        if (rc != 0)
                        {
                            goto end_allocate_page;
                        }
                        puint = iTrunk;
                        Buffer.BlockCopy(pTrunk.aData, 0, pPage1.aData, 32, 4);
                            //memcpy( pPage1.aData[32], ref pTrunk.aData[0], 4 );
                        ppPage = pTrunk;
                        pTrunk = null;
                        TRACE("ALLOCATE: %d trunk - %d free pages left\n", puint, n - 1);
                    }
                    else if (k > (uint) (pBt.usableSize/4 - 2))
                    {
                        /* Value of k is out of range.  Database corruption */
                        rc = UnitTest.SQLITE_CORRUPT_BKPT();
                        goto end_allocate_page;
#if !SQLITE_OMIT_AUTOVACUUM
                    }
                    else if (searchList != 0 && nearby == iTrunk)
                    {
                        /* The list is being searched and this trunk page is the page
            ** to allocate, regardless of whether it has leaves.
            */
                        Debug.Assert(puint == iTrunk);
                        ppPage = pTrunk;
                        searchList = 0;
                        rc = PagerHelper.PagerWrite(pTrunk.pDbPage);
                        if (rc != 0)
                        {
                            goto end_allocate_page;
                        }
                        if (k == 0)
                        {
                            if (null == pPrevTrunk)
                            {
                                //memcpy(pPage1.aData[32], pTrunk.aData[0], 4);
                                pPage1.aData[32 + 0] = pTrunk.aData[0 + 0];
                                pPage1.aData[32 + 1] = pTrunk.aData[0 + 1];
                                pPage1.aData[32 + 2] = pTrunk.aData[0 + 2];
                                pPage1.aData[32 + 3] = pTrunk.aData[0 + 3];
                            }
                            else
                            {
                                //memcpy(pPrevTrunk.aData[0], pTrunk.aData[0], 4);
                                pPrevTrunk.aData[0 + 0] = pTrunk.aData[0 + 0];
                                pPrevTrunk.aData[0 + 1] = pTrunk.aData[0 + 1];
                                pPrevTrunk.aData[0 + 2] = pTrunk.aData[0 + 2];
                                pPrevTrunk.aData[0 + 3] = pTrunk.aData[0 + 3];
                            }
                        }
                        else
                        {
                            /* The trunk page is required by the caller but it contains
              ** pointers to free-list leaves. The first leaf becomes a trunk
              ** page in this case.
              */
                            var pNewTrunk = new MemPage();
                            uint iNewTrunk = Utility.Sqlite3Get4byte(pTrunk.aData, 8);
                            if (iNewTrunk > mxPage)
                            {
                                rc = UnitTest.SQLITE_CORRUPT_BKPT();
                                goto end_allocate_page;
                            }
                            UnitTest.TestCase(iNewTrunk == mxPage);
                            rc = btreeGetPage(pBt, iNewTrunk, ref pNewTrunk, 0);
                            if (rc != StatusCode.SQLITE_OK)
                            {
                                goto end_allocate_page;
                            }
                            rc = PagerHelper.PagerWrite(pNewTrunk.pDbPage);
                            if (rc != StatusCode.SQLITE_OK)
                            {
                                releasePage(pNewTrunk);
                                goto end_allocate_page;
                            }
                            //memcpy(pNewTrunk.aData[0], pTrunk.aData[0], 4);
                            pNewTrunk.aData[0 + 0] = pTrunk.aData[0 + 0];
                            pNewTrunk.aData[0 + 1] = pTrunk.aData[0 + 1];
                            pNewTrunk.aData[0 + 2] = pTrunk.aData[0 + 2];
                            pNewTrunk.aData[0 + 3] = pTrunk.aData[0 + 3];
                            Utility.Sqlite3Put4byte(pNewTrunk.aData, (uint) 4, (k - 1));
                            Buffer.BlockCopy(pTrunk.aData, 12, pNewTrunk.aData, 8, (int) (k - 1)*4);
                                //memcpy( pNewTrunk.aData[8], ref pTrunk.aData[12], ( k - 1 ) * 4 );
                            releasePage(pNewTrunk);
                            if (null == pPrevTrunk)
                            {
                                Debug.Assert(sqlite3PagerIswriteable(pPage1.pDbPage));
                                Utility.Sqlite3Put4byte(pPage1.aData, (uint) 32, iNewTrunk);
                            }
                            else
                            {
                                rc = PagerHelper.PagerWrite(pPrevTrunk.pDbPage);
                                if (rc != 0)
                                {
                                    goto end_allocate_page;
                                }
                                Utility.Sqlite3Put4byte(pPrevTrunk.aData, (uint) 0, iNewTrunk);
                            }
                        }
                        pTrunk = null;
                        TRACE("ALLOCATE: %d trunk - %d free pages left\n", puint, n - 1);
#endif
                    }
                    else if (k > 0)
                    {
                        /* Extract a leaf from the trunk */
                        uint closest;
                        uint iPage;
                        byte[] aData = pTrunk.aData;
                        rc = PagerHelper.PagerWrite(pTrunk.pDbPage);
                        if (rc != 0)
                        {
                            goto end_allocate_page;
                        }
                        if (nearby > 0)
                        {
                            uint i;
                            int dist;
                            closest = 0;
                            dist = (int) (Utility.Sqlite3Get4byte(aData, 8) - nearby);
                            if (dist < 0) dist = -dist;
                            for (i = 1; i < k; i++)
                            {
                                var d2 = (int) (Utility.Sqlite3Get4byte(aData, 8 + i*4) - nearby);
                                if (d2 < 0) d2 = -d2;
                                if (d2 < dist)
                                {
                                    closest = i;
                                    dist = d2;
                                }
                            }
                        }
                        else
                        {
                            closest = 0;
                        }

                        iPage = Utility.Sqlite3Get4byte(aData, 8 + closest*4);
                        UnitTest.TestCase(iPage == mxPage);
                        if (iPage > mxPage)
                        {
                            rc = UnitTest.SQLITE_CORRUPT_BKPT();
                            goto end_allocate_page;
                        }
                        UnitTest.TestCase(iPage == mxPage);
                        if (0 == searchList || iPage == nearby)
                        {
                            int noContent;
                            puint = iPage;
                            TRACE("ALLOCATE: %d was leaf %d of %d on trunk %d" +
                                  ": %d more free pages\n",
                                  puint, closest + 1, k, pTrunk.pgno, n - 1);
                            if (closest < k - 1)
                            {
                                Buffer.BlockCopy(aData, (int) (4 + k*4), aData, 8 + (int) closest*4, 4);
                                    //memcpy( aData[8 + closest * 4], ref aData[4 + k * 4], 4 );
                            }
                            Utility.Sqlite3Put4byte(aData, (uint) 4, (k - 1)); // Utility.Sqlite3Put4byte( aData, 4, k - 1 );
                            Debug.Assert(sqlite3PagerIswriteable(pTrunk.pDbPage));
                            noContent = !btreeGetHasContent(pBt, puint) ? 1 : 0;
                            rc = btreeGetPage(pBt, puint, ref ppPage, noContent);
                            if (rc == StatusCode.SQLITE_OK)
                            {
                                rc = PagerHelper.PagerWrite((ppPage).pDbPage);
                                if (rc != StatusCode.SQLITE_OK)
                                {
                                    releasePage(ppPage);
                                }
                            }
                            searchList = 0;
                        }
                    }
                    releasePage(pPrevTrunk);
                    pPrevTrunk = null;
                } while (searchList != 0);
            }
            else
            {
                /* There are no pages on the freelist, so create a new page at the
        ** end of the file */
                var nPage = (int) pagerPagecount(pBt);
                puint = (uint) nPage + 1;

                if (puint == Utility.PENDING_BYTE_PAGE(pBt))
                {
                    (puint)++;
                }

#if !SQLITE_OMIT_AUTOVACUUM
                if (pBt.autoVacuum && PTRMAP_ISPAGE(pBt, puint))
                {
                    /* If puint refers to a pointer-map page, allocate two new pages
          ** at the end of the file instead of one. The first allocated page
          ** becomes a new pointer-map page, the second is used by the caller.
          */
                    MemPage pPg = null;
                    TRACE("ALLOCATE: %d from end of file (pointer-map page)\n", puint);
                    Debug.Assert(puint != Utility.PENDING_BYTE_PAGE(pBt));
                    rc = btreeGetPage(pBt, puint, ref pPg, 0);
                    if (rc == StatusCode.SQLITE_OK)
                    {
                        rc = PagerHelper.PagerWrite(pPg.pDbPage);
                        releasePage(pPg);
                    }
                    if (rc != 0) return rc;
                    (puint)++;
                    if (puint == Utility.PENDING_BYTE_PAGE(pBt))
                    {
                        (puint)++;
                    }
                }
#endif

                Debug.Assert(puint != Utility.PENDING_BYTE_PAGE(pBt));
                rc = btreeGetPage(pBt, puint, ref ppPage, 0);
                if (rc != 0) return rc;
                rc = PagerHelper.PagerWrite((ppPage).pDbPage);
                if (rc != StatusCode.SQLITE_OK)
                {
                    releasePage(ppPage);
                }
                TRACE("ALLOCATE: %d from end of file\n", puint);
            }

            Debug.Assert(puint != Utility.PENDING_BYTE_PAGE(pBt));

            end_allocate_page:
            releasePage(pTrunk);
            releasePage(pPrevTrunk);
            if (rc == StatusCode.SQLITE_OK)
            {
                if (sqlite3PagerPageRefcount((ppPage).pDbPage) > 1)
                {
                    releasePage(ppPage);
                    return UnitTest.SQLITE_CORRUPT_BKPT();
                }
                (ppPage).isInit = 0;
            }
            else
            {
                ppPage = null;
            }
            return rc;
        }

        /*
    ** This function is used to add page iPage to the database file free-list.
    ** It is assumed that the page is not already a part of the free-list.
    **
    ** The value passed as the second argument to this function is optional.
    ** If the caller happens to have a pointer to the MemPage object
    ** corresponding to page iPage handy, it may pass it as the second value.
    ** Otherwise, it may pass NULL.
    **
    ** If a pointer to a MemPage object is passed as the second argument,
    ** its reference count is not altered by this function.
    */

        private static int freePage2(BtShared pBt, MemPage pMemPage, uint iPage)
        {
            MemPage pTrunk = null; /* Free-list trunk page */
            uint iTrunk = 0; /* Page number of free-list trunk page */
            MemPage pPage1 = pBt.pPage1; /* Local reference to page 1 */
            MemPage pPage; /* Page being freed. May be NULL. */
            int rc; /* Return Code */
            int nFree; /* Initial number of pages on free-list */

            Debug.Assert(MutexHelper.MutexHeld(pBt.mutex));
            Debug.Assert(iPage > 1);
            Debug.Assert(null == pMemPage || pMemPage.pgno == iPage);

            if (pMemPage != null)
            {
                pPage = pMemPage;
                sqlite3PagerRef(pPage.pDbPage);
            }
            else
            {
                pPage = btreePageLookup(pBt, iPage);
            }

            /* Increment the free page count on pPage1 */
            rc = PagerHelper.PagerWrite(pPage1.pDbPage);
            if (rc != 0) goto freepage_out;
            nFree = (int) Utility.Sqlite3Get4byte(pPage1.aData, 36);
            Utility.Sqlite3Put4byte(pPage1.aData, 36, nFree + 1);

            if (pBt.secureDelete)
            {
                /* If the secure_delete option is enabled, then
        ** always fully overwrite deleted information with zeros.
        */
                if ((null == pPage && ((rc = btreeGetPage(pBt, iPage, ref pPage, 0)) != 0))
                    || ((rc = PagerHelper.PagerWrite(pPage.pDbPage)) != 0)
                    )
                {
                    goto freepage_out;
                }
                Array.Clear(pPage.aData, 0, pPage.pBt.pageSize); //memset(pPage->aData, 0, pPage->pBt->pageSize);
            }

            /* If the database supports auto-vacuum, write an entry in the pointer-map
** to indicate that the page is free.
*/
#if !SQLITE_OMIT_AUTOVACUUM
            //   if ( ISAUTOVACUUM )
            if (pBt.autoVacuum)
#else
if (false)
#endif
            {
                ptrmapPut(pBt, iPage, PTRMAP_FREEPAGE, 0, ref rc);
                if (rc != 0) goto freepage_out;
            }

            /* Now manipulate the actual database free-list structure. There are two
      ** possibilities. If the free-list is currently empty, or if the first
      ** trunk page in the free-list is full, then this page will become a
      ** new free-list trunk page. Otherwise, it will become a leaf of the
      ** first trunk page in the current free-list. This block tests if it
      ** is possible to add the page as a new free-list leaf.
      */
            if (nFree != 0)
            {
                uint nLeaf; /* Initial number of leaf cells on trunk page */

                iTrunk = Utility.Sqlite3Get4byte(pPage1.aData, 32);
                rc = btreeGetPage(pBt, iTrunk, ref pTrunk, 0);
                if (rc != StatusCode.SQLITE_OK)
                {
                    goto freepage_out;
                }

                nLeaf = Utility.Sqlite3Get4byte(pTrunk.aData, 4);
                Debug.Assert(pBt.usableSize > 32);
                if (nLeaf > (uint) pBt.usableSize/4 - 2)
                {
                    rc = UnitTest.SQLITE_CORRUPT_BKPT();
                    goto freepage_out;
                }
                if (nLeaf < (uint) pBt.usableSize/4 - 8)
                {
                    /* In this case there is room on the trunk page to insert the page
          ** being freed as a new leaf.
          **
          ** Note that the trunk page is not really full until it contains
          ** usableSize/4 - 2 entries, not usableSize/4 - 8 entries as we have
          ** coded.  But due to a coding error in versions of SQLite prior to
          ** 3.6.0, databases with freelist trunk pages holding more than
          ** usableSize/4 - 8 entries will be reported as corrupt.  In order
          ** to maintain backwards compatibility with older versions of SQLite,
          ** we will continue to restrict the number of entries to usableSize/4 - 8
          ** for now.  At some point in the future (once everyone has upgraded
          ** to 3.6.0 or later) we should consider fixing the conditional above
          ** to read "usableSize/4-2" instead of "usableSize/4-8".
          */
                    rc = PagerHelper.PagerWrite(pTrunk.pDbPage);
                    if (rc == StatusCode.SQLITE_OK)
                    {
                        Utility.Sqlite3Put4byte(pTrunk.aData, (uint) 4, nLeaf + 1);
                        Utility.Sqlite3Put4byte(pTrunk.aData, 8 + nLeaf*4, iPage);
                        if (pPage != null && !pBt.secureDelete)
                        {
                            sqlite3PagerDontWrite(pPage.pDbPage);
                        }
                        rc = btreeSetHasContent(pBt, iPage);
                    }
                    TRACE("FREE-PAGE: %d leaf on trunk page %d\n", iPage, pTrunk.pgno);
                    goto freepage_out;
                }
            }

            /* If control flows to this point, then it was not possible to add the
      ** the page being freed as a leaf page of the first trunk in the free-list.
      ** Possibly because the free-list is empty, or possibly because the
      ** first trunk in the free-list is full. Either way, the page being freed
      ** will become the new first trunk page in the free-list.
      */
            if (pPage == null && StatusCode.SQLITE_OK != (rc = btreeGetPage(pBt, iPage, ref pPage, 0)))
            {
                goto freepage_out;
            }
            rc = PagerHelper.PagerWrite(pPage.pDbPage);
            if (rc != StatusCode.SQLITE_OK)
            {
                goto freepage_out;
            }
            Utility.Sqlite3Put4byte(pPage.aData, iTrunk);
            Utility.Sqlite3Put4byte(pPage.aData, 4, 0);
            Utility.Sqlite3Put4byte(pPage1.aData, (uint) 32, iPage);
            TRACE("FREE-PAGE: %d new trunk page replacing %d\n", pPage.pgno, iTrunk);

            freepage_out:
            if (pPage != null)
            {
                pPage.isInit = 0;
            }
            releasePage(pPage);
            releasePage(pTrunk);
            return rc;
        }

        private static void freePage(MemPage pPage, ref int pRC)
        {
            if ((pRC) == StatusCode.SQLITE_OK)
            {
                pRC = freePage2(pPage.pBt, pPage, pPage.pgno);
            }
        }

        /*
    ** Free any overflow pages associated with the given Cell.
    */

        private static int clearCell(MemPage pPage, int pCell)
        {
            BtShared pBt = pPage.pBt;
            var info = new CellInfo();
            uint ovfluint;
            int rc;
            int nOvfl;
            ushort ovflPageSize;

            Debug.Assert(MutexHelper.MutexHeld(pPage.pBt.mutex));
            btreeParseCellPtr(pPage, pCell, ref info);
            if (info.iOverflow == 0)
            {
                return StatusCode.SQLITE_OK; /* No overflow pages. Return without doing anything */
            }
            ovfluint = Utility.Sqlite3Get4byte(pPage.aData, pCell, info.iOverflow);
            Debug.Assert(pBt.usableSize > 4);
            ovflPageSize = (ushort) (pBt.usableSize - 4);
            nOvfl = (int) ((info.nPayload - info.nLocal + ovflPageSize - 1)/ovflPageSize);
            Debug.Assert(ovfluint == 0 || nOvfl > 0);
            while (nOvfl-- != 0)
            {
                uint iNext = 0;
                MemPage pOvfl = null;
                if (ovfluint < 2 || ovfluint > pagerPagecount(pBt))
                {
                    /* 0 is not a legal page number and page 1 cannot be an
          ** overflow page. Therefore if ovfluint<2 or past the end of the
          ** file the database must be corrupt. */
                    return UnitTest.SQLITE_CORRUPT_BKPT();
                }
                if (nOvfl != 0)
                {
                    rc = getOverflowPage(pBt, ovfluint, ref pOvfl, ref iNext);
                    if (rc != 0) return rc;
                }

                if ((pOvfl != null || ((pOvfl = btreePageLookup(pBt, ovfluint)) != null))
                    && sqlite3PagerPageRefcount(pOvfl.pDbPage) != 1
                    )
                {
                    /* There is no reason any cursor should have an outstanding reference 
          ** to an overflow page belonging to a cell that is being deleted/updated.
          ** So if there exists more than one reference to this page, then it 
          ** must not really be an overflow page and the database must be corrupt. 
          ** It is helpful to detect this before calling freePage2(), as 
          ** freePage2() may zero the page contents if secure-delete mode is
          ** enabled. If this 'overflow' page happens to be a page that the
          ** caller is iterating through or using in some other way, this
          ** can be problematic.
          */
                    rc = UnitTest.SQLITE_CORRUPT_BKPT();
                }
                else
                {
                    rc = freePage2(pBt, pOvfl, ovfluint);
                }
                if (pOvfl != null)
                {
                    sqlite3PagerUnref(pOvfl.pDbPage);
                }
                if (rc != 0) return rc;
                ovfluint = iNext;
            }
            return StatusCode.SQLITE_OK;
        }

        /*
    ** Create the byte sequence used to represent a cell on page pPage
    ** and write that byte sequence into pCell[].  Overflow pages are
    ** allocated and filled in as necessary.  The calling procedure
    ** is responsible for making sure sufficient space has been allocated
    ** for pCell[].
    **
    ** Note that pCell does not necessary need to point to the pPage.aData
    ** area.  pCell might point to some temporary storage.  The cell will
    ** be constructed in this temporary area then copied into pPage.aData
    ** later.
    */

        private static int fillInCell(
            MemPage pPage, /* The page that contains the cell */
            byte[] pCell, /* Complete text of the cell */
            byte[] pKey, long nKey, /* The key */
            byte[] pData, int nData, /* The data */
            int nZero, /* Extra zero bytes to append to pData */
            ref int pnSize /* Write cell size here */
            )
        {
            int nPayload;
            byte[] pSrc;
            int pSrcIndex = 0;
            int nSrc, n, rc;
            int spaceLeft;
            MemPage pOvfl = null;
            MemPage pToRelease = null;
            byte[] pPrior;
            int pPriorIndex = 0;
            byte[] pPayload;
            int pPayloadIndex = 0;
            BtShared pBt = pPage.pBt;
            uint pgnoOvfl = 0;
            int nHeader;
            var info = new CellInfo();

            Debug.Assert(MutexHelper.MutexHeld(pPage.pBt.mutex));

            /* pPage is not necessarily writeable since pCell might be auxiliary
      ** buffer space that is separate from the pPage buffer area */
            // TODO -- Determine if the following Assert is needed under c#
            //Debug.Assert( pCell < pPage.aData || pCell >= &pPage.aData[pBt.pageSize]
            //          || sqlite3PagerIswriteable(pPage.pDbPage) );

            /* Fill in the header. */
            nHeader = 0;
            if (0 == pPage.leaf)
            {
                nHeader += 4;
            }
            if (pPage.hasData != 0)
            {
                nHeader +=Utility. PutVarint(pCell, nHeader, (nData + nZero)); //Utility.PutVarint( pCell[nHeader], nData + nZero );
            }
            else
            {
                nData = nZero = 0;
            }
            nHeader += Utility.PutVarint(pCell, nHeader, (ulong) nKey); //Utility.PutVarint( pCell[nHeader], *(ulong*)&nKey );
            btreeParseCellPtr(pPage, pCell, ref info);
            Debug.Assert(info.nHeader == nHeader);
            Debug.Assert(info.nKey == nKey);
            Debug.Assert(info.nData == (uint) (nData + nZero));

            /* Fill in the payload */
            nPayload = nData + nZero;
            if (pPage.intKey != 0)
            {
                pSrc = pData;
                nSrc = nData;
                nData = 0;
            }
            else
            {
                if (UnitTest.NEVER(nKey > 0x7fffffff || pKey == null))
                {
                    return UnitTest.SQLITE_CORRUPT_BKPT();
                }
                nPayload += (int) nKey;
                pSrc = pKey;
                nSrc = (int) nKey;
            }
            pnSize = info.nSize;
            spaceLeft = info.nLocal;
            //  pPayload = &pCell[nHeader];
            pPayload = pCell;
            pPayloadIndex = nHeader;
            //  pPrior = &pCell[info.iOverflow];
            pPrior = pCell;
            pPriorIndex = info.iOverflow;

            while (nPayload > 0)
            {
                if (spaceLeft == 0)
                {
#if !SQLITE_OMIT_AUTOVACUUM
                    uint pgnoPtrmap = pgnoOvfl; /* Overflow page pointer-map entry page */
                    if (pBt.autoVacuum)
                    {
                        do
                        {
                            pgnoOvfl++;
                        } while (
                            PTRMAP_ISPAGE(pBt, pgnoOvfl) || pgnoOvfl == Utility.PENDING_BYTE_PAGE(pBt)
                            );
                    }
#endif
                    rc = allocateBtreePage(pBt, ref pOvfl, ref pgnoOvfl, pgnoOvfl, 0);
#if !SQLITE_OMIT_AUTOVACUUM
                    /* If the database supports auto-vacuum, and the second or subsequent
** overflow page is being allocated, add an entry to the pointer-map
** for that page now.
**
** If this is the first overflow page, then write a partial entry
** to the pointer-map. If we write nothing to this pointer-map slot,
** then the optimistic overflow chain processing in clearCell()
** may misinterpret the uninitialised values and delete the
** wrong pages from the database.
*/
                    if (pBt.autoVacuum && rc == StatusCode.SQLITE_OK)
                    {
                        var eType = (byte) (pgnoPtrmap != 0 ? PTRMAP_OVERFLOW2 : PTRMAP_OVERFLOW1);
                        ptrmapPut(pBt, pgnoOvfl, eType, pgnoPtrmap, ref rc);
                        if (rc != 0)
                        {
                            releasePage(pOvfl);
                        }
                    }
#endif
                    if (rc != 0)
                    {
                        releasePage(pToRelease);
                        return rc;
                    }

                    /* If pToRelease is not zero than pPrior points into the data area
          ** of pToRelease.  Make sure pToRelease is still writeable. */
                    Debug.Assert(pToRelease == null || sqlite3PagerIswriteable(pToRelease.pDbPage));

                    /* If pPrior is part of the data area of pPage, then make sure pPage
          ** is still writeable */
                    // TODO -- Determine if the following Assert is needed under c#
                    //Debug.Assert( pPrior < pPage.aData || pPrior >= &pPage.aData[pBt.pageSize]
                    //      || sqlite3PagerIswriteable(pPage.pDbPage) );

                    Utility.Sqlite3Put4byte(pPrior, pPriorIndex, pgnoOvfl);
                    releasePage(pToRelease);
                    pToRelease = pOvfl;
                    pPrior = pOvfl.aData;
                    pPriorIndex = 0;
                    Utility.Sqlite3Put4byte(pPrior, 0);
                    pPayload = pOvfl.aData;
                    pPayloadIndex = 4; //&pOvfl.aData[4];
                    spaceLeft = pBt.usableSize - 4;
                }
                n = nPayload;
                if (n > spaceLeft) n = spaceLeft;

                /* If pToRelease is not zero than pPayload points into the data area
        ** of pToRelease.  Make sure pToRelease is still writeable. */
                Debug.Assert(pToRelease == null || sqlite3PagerIswriteable(pToRelease.pDbPage));

                /* If pPayload is part of the data area of pPage, then make sure pPage
        ** is still writeable */
                // TODO -- Determine if the following Assert is needed under c#
                //Debug.Assert( pPayload < pPage.aData || pPayload >= &pPage.aData[pBt.pageSize]
                //        || sqlite3PagerIswriteable(pPage.pDbPage) );

                if (nSrc > 0)
                {
                    if (n > nSrc) n = nSrc;
                    Debug.Assert(pSrc != null);
                    Buffer.BlockCopy(pSrc, pSrcIndex, pPayload, pPayloadIndex, n); //memcpy(pPayload, pSrc, n);
                }
                else
                {
                    byte[] pZeroBlob = Malloc.sqlite3Malloc(n); // memset(pPayload, 0, n);
                    Buffer.BlockCopy(pZeroBlob, 0, pPayload, pPayloadIndex, n);
                }
                nPayload -= n;
                pPayloadIndex += n; // pPayload += n;
                pSrcIndex += n; // pSrc += n;
                nSrc -= n;
                spaceLeft -= n;
                if (nSrc == 0)
                {
                    nSrc = nData;
                    pSrc = pData;
                }
            }
            releasePage(pToRelease);
            return StatusCode.SQLITE_OK;
        }

        /*
    ** Remove the i-th cell from pPage.  This routine effects pPage only.
    ** The cell content is not freed or deallocated.  It is assumed that
    ** the cell content has been copied someplace else.  This routine just
    ** removes the reference to the cell from pPage.
    **
    ** "sz" must be the number of bytes in the cell.
    */

        private static void dropCell(MemPage pPage, int idx, int sz, ref int pRC)
        {
            int i; /* Loop counter */
            int pc; /* Offset to cell content of cell being deleted */
            byte[] data; /* pPage.aData */
            int ptr; /* Used to move bytes around within data[] */
            int rc; /* The return code */
            int hdr; /* Beginning of the header.  0 most pages.  100 page 1 */

            if (pRC != 0) return;

            Debug.Assert(idx >= 0 && idx < pPage.nCell);
            Debug.Assert(sz == cellSize(pPage, idx));
            Debug.Assert(sqlite3PagerIswriteable(pPage.pDbPage));
            Debug.Assert(MutexHelper.MutexHeld(pPage.pBt.mutex));
            data = pPage.aData;
            ptr = pPage.cellOffset + 2*idx; //ptr = &data[pPage.cellOffset + 2 * idx];
            pc = get2byte(data, ptr);
            hdr = pPage.hdrOffset;
            UnitTest.TestCase(pc == get2byte(data, hdr + 5));
            UnitTest.TestCase(pc + sz == pPage.pBt.usableSize);
            if (pc < get2byte(data, hdr + 5) || pc + sz > pPage.pBt.usableSize)
            {
                pRC = UnitTest.SQLITE_CORRUPT_BKPT();
                return;
            }
            rc = freeSpace(pPage, pc, sz);
            if (rc != 0)
            {
                pRC = rc;
                return;
            }
            //for ( i = idx + 1 ; i < pPage.nCell ; i++, ptr += 2 )
            //{
            //  ptr[0] = ptr[2];
            //  ptr[1] = ptr[3];
            //}
            Buffer.BlockCopy(data, ptr + 2, data, ptr, (pPage.nCell - 1 - idx)*2);
            pPage.nCell--;
            data[pPage.hdrOffset + 3] = (byte) (pPage.nCell >> 8);
            data[pPage.hdrOffset + 4] = (byte) (pPage.nCell); //put2byte( data, hdr + 3, pPage.nCell );
            pPage.nFree += 2;
        }

        /*
    ** Insert a new cell on pPage at cell index "i".  pCell points to the
    ** content of the cell.
    **
    ** If the cell content will fit on the page, then put it there.  If it
    ** will not fit, then make a copy of the cell content into pTemp if
    ** pTemp is not null.  Regardless of pTemp, allocate a new entry
    ** in pPage.aOvfl[] and make it point to the cell content (either
    ** in pTemp or the original pCell) and also record its index.
    ** Allocating a new entry in pPage.aCell[] implies that
    ** pPage.nOverflow is incremented.
    **
    ** If nSkip is non-zero, then do not copy the first nSkip bytes of the
    ** cell. The caller will overwrite them after this function returns. If
    ** nSkip is non-zero, then pCell may not point to an invalid memory location
    ** (but pCell+nSkip is always valid).
    */

        private static void insertCell(
            MemPage pPage, /* Page into which we are copying */
            int i, /* New cell becomes the i-th cell of the page */
            byte[] pCell, /* Content of the new cell */
            int sz, /* Bytes of content in pCell */
            byte[] pTemp, /* Temp storage space for pCell, if needed */
            uint iChild, /* If non-zero, replace first 4 bytes with this value */
            ref int pRC /* Read and write return code from here */
            )
        {
            int idx = 0; /* Where to write new cell content in data[] */
            int j; /* Loop counter */
            int end; /* First byte past the last cell pointer in data[] */
            int ins; /* Index in data[] where new cell pointer is inserted */
            int cellOffset; /* Address of first cell pointer in data[] */
            byte[] data; /* The content of the whole page */
            byte ptr; /* Used for moving information around in data[] */

            int nSkip = (iChild != 0 ? 4 : 0);

            if (pRC != 0) return;

            Debug.Assert(i >= 0 && i <= pPage.nCell + pPage.nOverflow);
            Debug.Assert(pPage.nCell <= MX_CELL(pPage.pBt) && MX_CELL(pPage.pBt) <= 5460);
            Debug.Assert(pPage.nOverflow <= Utility.ArraySize(pPage.aOvfl));
            Debug.Assert(MutexHelper.MutexHeld(pPage.pBt.mutex));
            /* The cell should normally be sized correctly.  However, when moving a
      ** malformed cell from a leaf page to an interior page, if the cell size
      ** wanted to be less than 4 but got rounded up to 4 on the leaf, then size
      ** might be less than 8 (leaf-size + pointer) on the interior node.  Hence
      ** the term after the || in the following assert(). */
            Debug.Assert(sz == cellSizePtr(pPage, pCell) || (sz == 8 && iChild > 0));
            if (pPage.nOverflow != 0 || sz + 2 > pPage.nFree)
            {
                if (pTemp != null)
                {
                    Buffer.BlockCopy(pCell, nSkip, pTemp, nSkip, sz - nSkip);
                        //memcpy(pTemp+nSkip, pCell+nSkip, sz-nSkip);
                    pCell = pTemp;
                }
                if (iChild != 0)
                {
                    Utility.Sqlite3Put4byte(pCell, iChild);
                }
                j = pPage.nOverflow++;
                Debug.Assert(j < pPage.aOvfl.Length); //(int)(sizeof(pPage.aOvfl)/sizeof(pPage.aOvfl[0])) );
                pPage.aOvfl[j].pCell = pCell;
                pPage.aOvfl[j].idx = (ushort) i;
            }
            else
            {
                int rc = PagerHelper.PagerWrite(pPage.pDbPage);
                if (rc != StatusCode.SQLITE_OK)
                {
                    pRC = rc;
                    return;
                }
                Debug.Assert(sqlite3PagerIswriteable(pPage.pDbPage));
                data = pPage.aData;
                cellOffset = pPage.cellOffset;
                end = cellOffset + 2*pPage.nCell;
                ins = cellOffset + 2*i;
                rc = allocateSpace(pPage, sz, ref idx);
                if (rc != 0)
                {
                    pRC = rc;
                    return;
                }
                /* The allocateSpace() routine guarantees the following two properties
        ** if it returns success */
                Debug.Assert(idx >= end + 2);
                Debug.Assert(idx + sz <= pPage.pBt.usableSize);
                pPage.nCell++;
                pPage.nFree -= (ushort) (2 + sz);
                Buffer.BlockCopy(pCell, nSkip, data, idx + nSkip, sz - nSkip);
                    //memcpy( data[idx + nSkip], pCell + nSkip, sz - nSkip );
                if (iChild != 0)
                {
                    Utility.Sqlite3Put4byte(data, idx, iChild);
                }
                //for(j=end, ptr=&data[j]; j>ins; j-=2, ptr-=2){
                //  ptr[0] = ptr[-2];
                //  ptr[1] = ptr[-1];
                //}
                for (j = end; j > ins; j -= 2)
                {
                    data[j + 0] = data[j - 2];
                    data[j + 1] = data[j - 1];
                }
                put2byte(data, ins, idx);
                put2byte(data, pPage.hdrOffset + 3, pPage.nCell);
#if !SQLITE_OMIT_AUTOVACUUM
                if (pPage.pBt.autoVacuum)
                {
                    /* The cell may contain a pointer to an overflow page. If so, write
          ** the entry for the overflow page into the pointer map.
          */
                    ptrmapPutOvflPtr(pPage, pCell, ref pRC);
                }
#endif
            }
        }

        /*
    ** Add a list of cells to a page.  The page should be initially empty.
    ** The cells are guaranteed to fit on the page.
    */

        private static void assemblePage(
            MemPage pPage, /* The page to be assemblied */
            int nCell, /* The number of cells to add to this page */
            byte[] apCell, /* Pointer to a single the cell bodies */
            int[] aSize /* Sizes of the cells bodie*/
            )
        {
            int i; /* Loop counter */
            int pCellptr; /* Address of next cell pointer */
            int cellbody; /* Address of next cell body */
            byte[] data = pPage.aData; /* Pointer to data for pPage */
            int hdr = pPage.hdrOffset; /* Offset of header on pPage */
            int nUsable = pPage.pBt.usableSize; /* Usable size of page */

            Debug.Assert(pPage.nOverflow == 0);
            Debug.Assert(MutexHelper.MutexHeld(pPage.pBt.mutex));
            Debug.Assert(nCell >= 0 && nCell <= MX_CELL(pPage.pBt) && MX_CELL(pPage.pBt) <= 5460);
            Debug.Assert(sqlite3PagerIswriteable(pPage.pDbPage));

            /* Check that the page has just been zeroed by zeroPage() */
            Debug.Assert(pPage.nCell == 0);
            Debug.Assert(get2byte(data, hdr + 5) == nUsable);

            pCellptr = pPage.cellOffset + nCell*2; //data[pPage.cellOffset + nCell * 2];
            cellbody = nUsable;
            for (i = nCell - 1; i >= 0; i--)
            {
                pCellptr -= 2;
                cellbody -= aSize[i];
                put2byte(data, pCellptr, cellbody);
                Buffer.BlockCopy(apCell, 0, data, cellbody, aSize[i]);
                    //          memcpy(data[cellbody], apCell[i], aSize[i]);
            }
            put2byte(data, hdr + 3, nCell);
            put2byte(data, hdr + 5, cellbody);
            pPage.nFree -= (ushort) (nCell*2 + nUsable - cellbody);
            pPage.nCell = (ushort) nCell;
        }

        private static void assemblePage(
            MemPage pPage, /* The page to be assemblied */
            int nCell, /* The number of cells to add to this page */
            byte[][] apCell, /* Pointers to cell bodies */
            ushort[] aSize, /* Sizes of the cells */
            int offset /* Offset into the cell bodies, for c#  */
            )
        {
            int i; /* Loop counter */
            int pCellptr; /* Address of next cell pointer */
            int cellbody; /* Address of next cell body */
            byte[] data = pPage.aData; /* Pointer to data for pPage */
            int hdr = pPage.hdrOffset; /* Offset of header on pPage */
            int nUsable = pPage.pBt.usableSize; /* Usable size of page */

            Debug.Assert(pPage.nOverflow == 0);
            Debug.Assert(MutexHelper.MutexHeld(pPage.pBt.mutex));
            Debug.Assert(nCell >= 0 && nCell <= MX_CELL(pPage.pBt) && MX_CELL(pPage.pBt) <= 5460);
            Debug.Assert(sqlite3PagerIswriteable(pPage.pDbPage));

            /* Check that the page has just been zeroed by zeroPage() */
            Debug.Assert(pPage.nCell == 0);
            Debug.Assert(get2byte(data, hdr + 5) == nUsable);

            pCellptr = pPage.cellOffset + nCell*2; //data[pPage.cellOffset + nCell * 2];
            cellbody = nUsable;
            for (i = nCell - 1; i >= 0; i--)
            {
                pCellptr -= 2;
                cellbody -= aSize[i + offset];
                put2byte(data, pCellptr, cellbody);
                Buffer.BlockCopy(apCell[offset + i], 0, data, cellbody, aSize[i + offset]);
                    //          memcpy(&data[cellbody], apCell[i], aSize[i]);
            }
            put2byte(data, hdr + 3, nCell);
            put2byte(data, hdr + 5, cellbody);
            pPage.nFree -= (ushort) (nCell*2 + nUsable - cellbody);
            pPage.nCell = (ushort) nCell;
        }

        private static void assemblePage(
            MemPage pPage, /* The page to be assemblied */
            int nCell, /* The number of cells to add to this page */
            byte[] apCell, /* Pointers to cell bodies */
            ushort[] aSize /* Sizes of the cells */
            )
        {
            int i; /* Loop counter */
            int pCellptr; /* Address of next cell pointer */
            int cellbody; /* Address of next cell body */
            byte[] data = pPage.aData; /* Pointer to data for pPage */
            int hdr = pPage.hdrOffset; /* Offset of header on pPage */
            int nUsable = pPage.pBt.usableSize; /* Usable size of page */

            Debug.Assert(pPage.nOverflow == 0);
            Debug.Assert(MutexHelper.MutexHeld(pPage.pBt.mutex));
            Debug.Assert(nCell >= 0 && nCell <= MX_CELL(pPage.pBt) && MX_CELL(pPage.pBt) <= 5460);
            Debug.Assert(sqlite3PagerIswriteable(pPage.pDbPage));

            /* Check that the page has just been zeroed by zeroPage() */
            Debug.Assert(pPage.nCell == 0);
            Debug.Assert(get2byte(data, hdr + 5) == nUsable);

            pCellptr = pPage.cellOffset + nCell*2; //&data[pPage.cellOffset + nCell * 2];
            cellbody = nUsable;
            for (i = nCell - 1; i >= 0; i--)
            {
                pCellptr -= 2;
                cellbody -= aSize[i];
                put2byte(data, pCellptr, cellbody);
                Buffer.BlockCopy(apCell, 0, data, cellbody, aSize[i]); //memcpy( data[cellbody], apCell[i], aSize[i] );
            }
            put2byte(data, hdr + 3, nCell);
            put2byte(data, hdr + 5, cellbody);
            pPage.nFree -= (ushort) (nCell*2 + nUsable - cellbody);
            pPage.nCell = (ushort) nCell;
        }

        /*
    ** The following parameters determine how many adjacent pages get involved
    ** in a balancing operation.  NN is the number of neighbors on either side
    ** of the page that participate in the balancing operation.  NB is the
    ** total number of pages that participate, including the target page and
    ** NN neighbors on either side.
    **
    ** The minimum value of NN is 1 (of course).  Increasing NN above 1
    ** (to 2 or 3) gives a modest improvement in SELECT and DELETE performance
    ** in exchange for a larger degradation in INSERT and UPDATE performance.
    ** The value of NN appears to give the best results overall.
    */
        private static int NN = 1; /* Number of neighbors on either side of pPage */
        private static readonly int NB = (NN*2 + 1); /* Total pages involved in the balance */

#if !SQLITE_OMIT_QUICKBALANCE
        /*
** This version of balance() handles the common special case where
** a new entry is being inserted on the extreme right-end of the
** tree, in other words, when the new entry will become the largest
** entry in the tree.
**
** Instead of trying to balance the 3 right-most leaf pages, just add
** a new page to the right-hand side and put the one new entry in
** that page.  This leaves the right side of the tree somewhat
** unbalanced.  But odds are that we will be inserting new entries
** at the end soon afterwards so the nearly empty page will quickly
** fill up.  On average.
**
** pPage is the leaf page which is the right-most page in the tree.
** pParent is its parent.  pPage must have a single overflow entry
** which is also the right-most entry on the page.
**
** The pSpace buffer is used to store a temporary copy of the divider
** cell that will be inserted into pParent. Such a cell consists of a 4
** byte page number followed by a variable length integer. In other
** words, at most 13 bytes. Hence the pSpace buffer must be at
** least 13 bytes in size.
*/

        private static int balance_quick(MemPage pParent, MemPage pPage, byte[] pSpace)
        {
            BtShared pBt = pPage.pBt; /* B-Tree Database */
            var pNew = new MemPage(); /* Newly allocated page */
            int rc; /* Return Code */
            uint pgnoNew = 0; /* Page number of pNew */

            Debug.Assert(MutexHelper.MutexHeld(pPage.pBt.mutex));
            Debug.Assert(sqlite3PagerIswriteable(pParent.pDbPage));
            Debug.Assert(pPage.nOverflow == 1);

            if (pPage.nCell <= 0)
                return UnitTest.SQLITE_CORRUPT_BKPT();

            /* Allocate a new page. This page will become the right-sibling of
** pPage. Make the parent page writable, so that the new divider cell
** may be inserted. If both these operations are successful, proceed.
*/
            rc = allocateBtreePage(pBt, ref pNew, ref pgnoNew, 0, 0);

            if (rc == StatusCode.SQLITE_OK)
            {
                int pOut = 4; //byte pOut = &pSpace[4];
                byte[] pCell = pPage.aOvfl[0].pCell;
                var szCell = new int[1];
                szCell[0] = cellSizePtr(pPage, pCell);
                int pStop;

                Debug.Assert(sqlite3PagerIswriteable(pNew.pDbPage));
                Debug.Assert(pPage.aData[0] == (PageTypeFlag.PTF_INTKEY | PageTypeFlag.PTF_LEAFDATA | PageTypeFlag.PTF_LEAF));
                zeroPage(pNew, PageTypeFlag.PTF_INTKEY | PageTypeFlag.PTF_LEAFDATA | PageTypeFlag.PTF_LEAF);
                assemblePage(pNew, 1, pCell, szCell);

                /* If this is an auto-vacuum database, update the pointer map
        ** with entries for the new page, and any pointer from the
        ** cell on the page to an overflow page. If either of these
        ** operations fails, the return code is set, but the contents
        ** of the parent page are still manipulated by thh code below.
        ** That is Ok, at this point the parent page is guaranteed to
        ** be marked as dirty. Returning an error code will cause a
        ** rollback, undoing any changes made to the parent page.
        */
#if !SQLITE_OMIT_AUTOVACUUM
                //   if ( ISAUTOVACUUM )
                if (pBt.autoVacuum)
#else
if (false)
#endif
                {
                    ptrmapPut(pBt, pgnoNew, PTRMAP_BTREE, pParent.pgno, ref rc);
                    if (szCell[0] > pNew.minLocal)
                    {
                        ptrmapPutOvflPtr(pNew, pCell, ref rc);
                    }
                }

                /* Create a divider cell to insert into pParent. The divider cell
        ** consists of a 4-byte page number (the page number of pPage) and
        ** a variable length key value (which must be the same value as the
        ** largest key on pPage).
        **
        ** To find the largest key value on pPage, first find the right-most
        ** cell on pPage. The first two fields of this cell are the
        ** record-length (a variable length integer at most 32-bits in size)
        ** and the key value (a variable length integer, may have any value).
        ** The first of the while(...) loops below skips over the record-length
        ** field. The second while(...) loop copies the key value from the
        ** cell on pPage into the pSpace buffer.
        */
                int iCell = findCell(pPage, pPage.nCell - 1); //pCell = findCell( pPage, pPage.nCell - 1 );
                pCell = pPage.aData;
                int _pCell = iCell;
                pStop = _pCell + 9; //pStop = &pCell[9];
                while (((pCell[_pCell++]) & 0x80) != 0 && _pCell < pStop)
                    ; //while ( ( *( pCell++ ) & 0x80 ) && pCell < pStop ) ;
                pStop = _pCell + 9; //pStop = &pCell[9];
                while (((pSpace[pOut++] = pCell[_pCell++]) & 0x80) != 0 && _pCell < pStop)
                    ; //while ( ( ( *( pOut++ ) = *( pCell++ ) ) & 0x80 ) && pCell < pStop ) ;

                /* Insert the new divider cell into pParent. */
                insertCell(pParent, pParent.nCell, pSpace, pOut, //(int)(pOut-pSpace),
                           null, pPage.pgno, ref rc);

                /* Set the right-child pointer of pParent to point to the new page. */
                Utility.Sqlite3Put4byte(pParent.aData, pParent.hdrOffset + 8, pgnoNew);

                /* Release the reference to the new page. */
                releasePage(pNew);
            }

            return rc;
        }
#endif
        //* SQLITE_OMIT_QUICKBALANCE */

#if FALSE
/*
** This function does not contribute anything to the operation of SQLite.
** it is sometimes activated temporarily while debugging code responsible
** for setting pointer-map entries.
*/
static int ptrmapCheckPages(MemPage **apPage, int nPage){
int i, j;
for(i=0; i<nPage; i++){
uint n;
byte e;
MemPage pPage = apPage[i];
BtShared pBt = pPage.pBt;
Debug.Assert( pPage.isInit!=0 );

for(j=0; j<pPage.nCell; j++){
CellInfo info;
byte *z;

z = findCell(pPage, j);
btreeParseCellPtr(pPage, z,  info);
if( info.iOverflow ){
uint ovfl = Utility.Sqlite3Get4byte(z[info.iOverflow]);
ptrmapGet(pBt, ovfl, ref e, ref n);
Debug.Assert( n==pPage.pgno && e==PTRMAP_OVERFLOW1 );
}
if( 0==pPage.leaf ){
uint child = Utility.Sqlite3Get4byte(z);
ptrmapGet(pBt, child, ref e, ref n);
Debug.Assert( n==pPage.pgno && e==PTRMAP_BTREE );
}
}
if( 0==pPage.leaf ){
uint child = Utility.Sqlite3Get4byte(pPage.aData,pPage.hdrOffset+8]);
ptrmapGet(pBt, child, ref e, ref n);
Debug.Assert( n==pPage.pgno && e==PTRMAP_BTREE );
}
}
return 1;
}
#endif

        /*
** This function is used to copy the contents of the b-tree node stored
** on page pFrom to page pTo. If page pFrom was not a leaf page, then
** the pointer-map entries for each child page are updated so that the
** parent page stored in the pointer map is page pTo. If pFrom contained
** any cells with overflow page pointers, then the corresponding pointer
** map entries are also updated so that the parent page is page pTo.
**
** If pFrom is currently carrying any overflow cells (entries in the
** MemPage.aOvfl[] array), they are not copied to pTo.
**
** Before returning, page pTo is reinitialized using btreeInitPage().
**
** The performance of this function is not critical. It is only used by
** the balance_shallower() and balance_deeper() procedures, neither of
** which are called often under normal circumstances.
*/

        private static void copyNodeContent(MemPage pFrom, MemPage pTo, ref int pRC)
        {
            if ((pRC) == StatusCode.SQLITE_OK)
            {
                BtShared pBt = pFrom.pBt;
                byte[] aFrom = pFrom.aData;
                byte[] aTo = pTo.aData;
                int iFromHdr = pFrom.hdrOffset;
                int iToHdr = ((pTo.pgno == 1) ? 100 : 0);
                int rc;
                int iData;


                Debug.Assert(pFrom.isInit != 0);
                Debug.Assert(pFrom.nFree >= iToHdr);
                Debug.Assert(get2byte(aFrom, iFromHdr + 5) <= pBt.usableSize);

                /* Copy the b-tree node content from page pFrom to page pTo. */
                iData = get2byte(aFrom, iFromHdr + 5);
                Buffer.BlockCopy(aFrom, iData, aTo, iData, pBt.usableSize - iData);
                    //memcpy(aTo[iData], ref aFrom[iData], pBt.usableSize-iData);
                Buffer.BlockCopy(aFrom, iFromHdr, aTo, iToHdr, pFrom.cellOffset + 2*pFrom.nCell);
                    //memcpy(aTo[iToHdr], ref aFrom[iFromHdr], pFrom.cellOffset + 2*pFrom.nCell);

                /* Reinitialize page pTo so that the contents of the MemPage structure
        ** match the new data. The initialization of pTo can actually fail under
        ** fairly obscure circumstances, even though it is a copy of initialized 
        ** page pFrom.
        */
                pTo.isInit = 0;
                rc = btreeInitPage(pTo);
                if (rc != StatusCode.SQLITE_OK)
                {
                    pRC = rc;
                    return;
                }

                /* If this is an auto-vacuum database, update the pointer-map entries
        ** for any b-tree or overflow pages that pTo now contains the pointers to.
        */
#if !SQLITE_OMIT_AUTOVACUUM
                //   if ( ISAUTOVACUUM )
                if (pBt.autoVacuum)
#else
if (false)
#endif
                {
                    pRC = setChildPtrmaps(pTo);
                }
            }
        }

        /*
    ** This routine redistributes cells on the iParentIdx'th child of pParent
    ** (hereafter "the page") and up to 2 siblings so that all pages have about the
    ** same amount of free space. Usually a single sibling on either side of the
    ** page are used in the balancing, though both siblings might come from one
    ** side if the page is the first or last child of its parent. If the page
    ** has fewer than 2 siblings (something which can only happen if the page
    ** is a root page or a child of a root page) then all available siblings
    ** participate in the balancing.
    **
    ** The number of siblings of the page might be increased or decreased by
    ** one or two in an effort to keep pages nearly full but not over full.
    **
    ** Note that when this routine is called, some of the cells on the page
    ** might not actually be stored in MemPage.aData[]. This can happen
    ** if the page is overfull. This routine ensures that all cells allocated
    ** to the page and its siblings fit into MemPage.aData[] before returning.
    **
    ** In the course of balancing the page and its siblings, cells may be
    ** inserted into or removed from the parent page (pParent). Doing so
    ** may cause the parent page to become overfull or underfull. If this
    ** happens, it is the responsibility of the caller to invoke the correct
    ** balancing routine to fix this problem (see the balance() routine).
    **
    ** If this routine fails for any reason, it might leave the database
    ** in a corrupted state. So if this routine fails, the database should
    ** be rolled back.
    **
    ** The third argument to this function, aOvflSpace, is a pointer to a
    ** buffer big enough to hold one page. If while inserting cells into the parent
    ** page (pParent) the parent page becomes overfull, this buffer is
    ** used to store the parent's overflow cells. Because this function inserts
    ** a maximum of four divider cells into the parent page, and the maximum
    ** size of a cell stored within an internal node is always less than 1/4
    ** of the page-size, the aOvflSpace[] buffer is guaranteed to be large
    ** enough for all overflow cells.
    **
    ** If aOvflSpace is set to a null pointer, this function returns
    ** StatusCode.SQLITE_NOMEM.
    */

        // under C#; Try to reuse Memory

        private static int balance_nonroot(
            MemPage pParent, /* Parent page of siblings being balanced */
            int iParentIdx, /* Index of "the page" in pParent */
            byte[] aOvflSpace, /* page-size bytes of space for parent ovfl */
            int isRoot /* True if pParent is a root-page */
            )
        {
            var apOld = new MemPage[NB]; /* pPage and up to two siblings */
            var apCopy = new MemPage[NB]; /* Private copies of apOld[] pages */
            var apNew = new MemPage[NB + 2]; /* pPage and up to NB siblings after balancing */
            var apDiv = new int[NB - 1]; /* Divider cells in pParent */
            var cntNew = new int[NB + 2]; /* Index in aCell[] of cell after i-th page */
            var szNew = new int[NB + 2]; /* Combined size of cells place on i-th page */
            var szCell = new ushort[1]; /* Local size of all cells in apCell[] */
            BtShared pBt; /* The whole database */
            int nCell = 0; /* Number of cells in apCell[] */
            int nMaxCells = 0; /* Allocated size of apCell, szCell, aFrom. */
            int nNew = 0; /* Number of pages in apNew[] */
            int nOld; /* Number of pages in apOld[] */
            int i, j, k; /* Loop counters */
            int nxDiv; /* Next divider slot in pParent.aCell[] */
            int rc = StatusCode.SQLITE_OK; /* The return code */
            ushort leafCorrection; /* 4 if pPage is a leaf.  0 if not */
            int leafData; /* True if pPage is a leaf of a LEAFDATA tree */
            int usableSpace; /* Bytes in pPage beyond the header */
            int pageFlags; /* Value of pPage.aData[0] */
            int subtotal; /* Subtotal of bytes in cells on one page */
            //int iSpace1 = 0;             /* First unused byte of aSpace1[] */
            int iOvflSpace = 0; /* First unused byte of aOvflSpace[] */
            int szScratch; /* Size of scratch memory requested */
            //MemPage[] apOld = new MemPage[NB];    /* pPage and up to two siblings */
            //MemPage[] apCopy = new MemPage[NB];   /* Private copies of apOld[] pages */
            //MemPage[] apNew = new MemPage[NB + 2];/* pPage and up to NB siblings after balancing */
            int pRight; /* Location in parent of right-sibling pointer */
            //int[] apDiv = new int[NB - 1];        /* Divider cells in pParent */
            //int[] cntNew = new int[NB + 2];       /* Index in aCell[] of cell after i-th page */
            //int[] szNew = new int[NB + 2];        /* Combined size of cells place on i-th page */
            byte[][] apCell = null; /* All cells begin balanced */
            //ushort[] szCell;                         /* Local size of all cells in apCell[] */
            //byte[] aSpace1;                         /* Space for copies of dividers cells */
            uint pgno; /* Temp var to store a page number in */

            pBt = pParent.pBt;
            Debug.Assert(MutexHelper.MutexHeld(pBt.mutex));
            Debug.Assert(sqlite3PagerIswriteable(pParent.pDbPage));

#if FALSE
TRACE("BALANCE: begin page %d child of %d\n", pPage.pgno, pParent.pgno);
#endif

            /* At this point pParent may have at most one overflow cell. And if
** this overflow cell is present, it must be the cell with
** index iParentIdx. This scenario comes about when this function
** is called (indirectly) from BTreeHelper.BtreeDelete().
*/
            Debug.Assert(pParent.nOverflow == 0 || pParent.nOverflow == 1);
            Debug.Assert(pParent.nOverflow == 0 || pParent.aOvfl[0].idx == iParentIdx);

            //if( !aOvflSpace ){
            //  return StatusCode.SQLITE_NOMEM;
            //}

            /* Find the sibling pages to balance. Also locate the cells in pParent
      ** that divide the siblings. An attempt is made to find NN siblings on
      ** either side of pPage. More siblings are taken from one side, however,
      ** if there are fewer than NN siblings on the other side. If pParent
      ** has NB or fewer children then all children of pParent are taken.
      **
      ** This loop also drops the divider cells from the parent page. This
      ** way, the remainder of the function does not have to deal with any
      ** overflow cells in the parent page, since if any existed they will
      ** have already been removed.
      */
            i = pParent.nOverflow + pParent.nCell;
            if (i < 2)
            {
                nxDiv = 0;
                nOld = i + 1;
            }
            else
            {
                nOld = 3;
                if (iParentIdx == 0)
                {
                    nxDiv = 0;
                }
                else if (iParentIdx == i)
                {
                    nxDiv = i - 2;
                }
                else
                {
                    nxDiv = iParentIdx - 1;
                }
                i = 2;
            }
            if ((i + nxDiv - pParent.nOverflow) == pParent.nCell)
            {
                pRight = pParent.hdrOffset + 8; //&pParent.aData[pParent.hdrOffset + 8];
            }
            else
            {
                pRight = findCell(pParent, i + nxDiv - pParent.nOverflow);
            }
            pgno = Utility.Sqlite3Get4byte(pParent.aData, pRight);
            while (true)
            {
                rc = getAndInitPage(pBt, pgno, ref apOld[i]);
                if (rc != 0)
                {
                    apOld = new MemPage[i + 1]; //memset(apOld, 0, (i+1)*sizeof(MemPage*));
                    goto balance_cleanup;
                }
                nMaxCells += 1 + apOld[i].nCell + apOld[i].nOverflow;
                if ((i--) == 0) break;

                if (i + nxDiv == pParent.aOvfl[0].idx && pParent.nOverflow != 0)
                {
                    apDiv[i] = 0; // = pParent.aOvfl[0].pCell;
                    pgno = Utility.Sqlite3Get4byte(pParent.aOvfl[0].pCell, apDiv[i]);
                    szNew[i] = cellSizePtr(pParent, apDiv[i]);
                    pParent.nOverflow = 0;
                }
                else
                {
                    apDiv[i] = findCell(pParent, i + nxDiv - pParent.nOverflow);
                    pgno = Utility.Sqlite3Get4byte(pParent.aData, apDiv[i]);
                    szNew[i] = cellSizePtr(pParent, apDiv[i]);

                    /* Drop the cell from the parent page. apDiv[i] still points to
          ** the cell within the parent, even though it has been dropped.
          ** This is safe because dropping a cell only overwrites the first
          ** four bytes of it, and this function does not need the first
          ** four bytes of the divider cell. So the pointer is safe to use
          ** later on.
          **
          ** Unless SQLite is compiled in secure-delete mode. In this case,
          ** the dropCell() routine will overwrite the entire cell with zeroes.
          ** In this case, temporarily copy the cell into the aOvflSpace[]
          ** buffer. It will be copied out again as soon as the aSpace[] buffer
          ** is allocated.  */
                    //if (pBt.secureDelete)
                    //{
                    //  int iOff = (int)(apDiv[i]) - (int)(pParent.aData); //SQLITE_PTR_TO_INT(apDiv[i]) - SQLITE_PTR_TO_INT(pParent.aData);
                    //  if ((iOff + szNew[i]) > pBt.usableSize)
                    //  {
                    //    rc = UnitTest.SQLITE_CORRUPT_BKPT();
                    //    Array.Clear(apOld[0].aData,0,apOld[0].aData.Length); //memset(apOld, 0, (i + 1) * sizeof(MemPage*));
                    //    goto balance_cleanup;
                    //  }
                    //  else
                    //  {
                    //    memcpy(&aOvflSpace[iOff], apDiv[i], szNew[i]);
                    //    apDiv[i] = &aOvflSpace[apDiv[i] - pParent.aData];
                    //  }
                    //}
                    dropCell(pParent, i + nxDiv - pParent.nOverflow, szNew[i], ref rc);
                }
            }

            /* Make nMaxCells a multiple of 4 in order to preserve 8-byte
      ** alignment */
            nMaxCells = (nMaxCells + 3) & ~3;

            /*
      ** Allocate space for memory structures
      */
            //k = pBt.pageSize + Utility.ROUND8(sizeof(MemPage));
            //szScratch =
            //     nMaxCells*sizeof(byte*)                       /* apCell */
            //   + nMaxCells*sizeof(ushort)                       /* szCell */
            //   + pBt.pageSize                               /* aSpace1 */
            //   + k*nOld;                                     /* Page copies (apCopy) */
            apCell = Malloc.ScratchMalloc(apCell, nMaxCells);
            //if( apCell==null ){
            //  rc = StatusCode.SQLITE_NOMEM;
            //  goto balance_cleanup;
            //}
            if (szCell.Length < nMaxCells) Array.Resize(ref szCell, nMaxCells); //(ushort*)&apCell[nMaxCells];
            //aSpace1 = new byte[pBt.pageSize * (nMaxCells)];//  aSpace1 = (byte*)&szCell[nMaxCells];
            //Debug.Assert( EIGHT_BYTE_ALIGNMENT(aSpace1) );

            /*
      ** Load pointers to all cells on sibling pages and the divider cells
      ** into the local apCell[] array.  Make copies of the divider cells
      ** into space obtained from aSpace1[] and remove the the divider Cells
      ** from pParent.
      **
      ** If the siblings are on leaf pages, then the child pointers of the
      ** divider cells are stripped from the cells before they are copied
      ** into aSpace1[].  In this way, all cells in apCell[] are without
      ** child pointers.  If siblings are not leaves, then all cell in
      ** apCell[] include child pointers.  Either way, all cells in apCell[]
      ** are alike.
      **
      ** leafCorrection:  4 if pPage is a leaf.  0 if pPage is not a leaf.
      **       leafData:  1 if pPage holds key+data and pParent holds only keys.
      */
            leafCorrection = (ushort) (apOld[0].leaf*4);
            leafData = apOld[0].hasData;
            for (i = 0; i < nOld; i++)
            {
                int limit;

                /* Before doing anything else, take a copy of the i'th original sibling
        ** The rest of this function will use data from the copies rather
        ** that the original pages since the original pages will be in the
        ** process of being overwritten.  */
                //MemPage pOld = apCopy[i] = (MemPage*)&aSpace1[pBt.pageSize + k*i];
                //memcpy(pOld, apOld[i], sizeof(MemPage));
                //pOld.aData = (void*)&pOld[1];
                //memcpy(pOld.aData, apOld[i].aData, pBt.pageSize);
                MemPage pOld = apCopy[i] = apOld[i].Copy();

                limit = pOld.nCell + pOld.nOverflow;
                for (j = 0; j < limit; j++)
                {
                    Debug.Assert(nCell < nMaxCells);
                    //apCell[nCell] = findOverflowCell( pOld, j );
                    //szCell[nCell] = cellSizePtr( pOld, apCell, nCell );
                    int iFOFC = findOverflowCell(pOld, j);
                    szCell[nCell] = cellSizePtr(pOld, iFOFC);
                    // Copy the Data Locally
                    if (apCell[nCell] == null) apCell[nCell] = new byte[szCell[nCell]];
                    else if (apCell[nCell].Length < szCell[nCell]) Array.Resize(ref apCell[nCell], szCell[nCell]);
                    if (iFOFC < 0) // Overflow Cell
                        Buffer.BlockCopy(pOld.aOvfl[-(iFOFC + 1)].pCell, 0, apCell[nCell], 0, szCell[nCell]);
                    else
                        Buffer.BlockCopy(pOld.aData, iFOFC, apCell[nCell], 0, szCell[nCell]);
                    nCell++;
                }
                if (i < nOld - 1 && 0 == leafData)
                {
                    var sz = (ushort) szNew[i];
                    byte[] pTemp = Malloc.sqlite3Malloc(sz + leafCorrection);
                    Debug.Assert(nCell < nMaxCells);
                    szCell[nCell] = sz;
                    //pTemp = &aSpace1[iSpace1];
                    //iSpace1 += sz;
                    Debug.Assert(sz <= pBt.pageSize/4);
                    //Debug.Assert(iSpace1 <= pBt.pageSize);
                    Buffer.BlockCopy(pParent.aData, apDiv[i], pTemp, 0, sz); //memcpy( pTemp, apDiv[i], sz );
                    if (apCell[nCell] == null || apCell[nCell].Length < sz) Array.Resize(ref apCell[nCell], sz);
                    Buffer.BlockCopy(pTemp, leafCorrection, apCell[nCell], 0, sz);
                        //apCell[nCell] = pTemp + leafCorrection;
                    Debug.Assert(leafCorrection == 0 || leafCorrection == 4);
                    szCell[nCell] = (ushort) (szCell[nCell] - leafCorrection);
                    if (0 == pOld.leaf)
                    {
                        Debug.Assert(leafCorrection == 0);
                        Debug.Assert(pOld.hdrOffset == 0);
                        /* The right pointer of the child page pOld becomes the left
            ** pointer of the divider cell */
                        Buffer.BlockCopy(pOld.aData, 8, apCell[nCell], 0, 4);
                            //memcpy( apCell[nCell], ref pOld.aData[8], 4 );
                    }
                    else
                    {
                        Debug.Assert(leafCorrection == 4);
                        if (szCell[nCell] < 4)
                        {
                            /* Do not allow any cells smaller than 4 bytes. */
                            szCell[nCell] = 4;
                        }
                    }
                    nCell++;
                }
            }

            /*
      ** Figure out the number of pages needed to hold all nCell cells.
      ** Store this number in "k".  Also compute szNew[] which is the total
      ** size of all cells on the i-th page and cntNew[] which is the index
      ** in apCell[] of the cell that divides page i from page i+1.
      ** cntNew[k] should equal nCell.
      **
      ** Values computed by this block:
      **
      **           k: The total number of sibling pages
      **    szNew[i]: Spaced used on the i-th sibling page.
      **   cntNew[i]: Index in apCell[] and szCell[] for the first cell to
      **              the right of the i-th sibling page.
      ** usableSpace: Number of bytes of space available on each sibling.
      **
      */
            usableSpace = pBt.usableSize - 12 + leafCorrection;
            for (subtotal = k = i = 0; i < nCell; i++)
            {
                Debug.Assert(i < nMaxCells);
                subtotal += szCell[i] + 2;
                if (subtotal > usableSpace)
                {
                    szNew[k] = subtotal - szCell[i];
                    cntNew[k] = i;
                    if (leafData != 0)
                    {
                        i--;
                    }
                    subtotal = 0;
                    k++;
                    if (k > NB + 1)
                    {
                        rc = UnitTest.SQLITE_CORRUPT_BKPT();
                        goto balance_cleanup;
                    }
                }
            }
            szNew[k] = subtotal;
            cntNew[k] = nCell;
            k++;

            /*
      ** The packing computed by the previous block is biased toward the siblings
      ** on the left side.  The left siblings are always nearly full, while the
      ** right-most sibling might be nearly empty.  This block of code attempts
      ** to adjust the packing of siblings to get a better balance.
      **
      ** This adjustment is more than an optimization.  The packing above might
      ** be so out of balance as to be illegal.  For example, the right-most
      ** sibling might be completely empty.  This adjustment is not optional.
      */
            for (i = k - 1; i > 0; i--)
            {
                int szRight = szNew[i]; /* Size of sibling on the right */
                int szLeft = szNew[i - 1]; /* Size of sibling on the left */
                int r; /* Index of right-most cell in left sibling */
                int d; /* Index of first cell to the left of right sibling */

                r = cntNew[i - 1] - 1;
                d = r + 1 - leafData;
                Debug.Assert(d < nMaxCells);
                Debug.Assert(r < nMaxCells);
                while (szRight == 0 || szRight + szCell[d] + 2 <= szLeft - (szCell[r] + 2))
                {
                    szRight += szCell[d] + 2;
                    szLeft -= szCell[r] + 2;
                    cntNew[i - 1]--;
                    r = cntNew[i - 1] - 1;
                    d = r + 1 - leafData;
                }
                szNew[i] = szRight;
                szNew[i - 1] = szLeft;
            }

            /* Either we found one or more cells (cntnew[0])>0) or pPage is
      ** a virtual root page.  A virtual root page is when the real root
      ** page is page 1 and we are the only child of that page.
      */
            Debug.Assert(cntNew[0] > 0 || (pParent.pgno == 1 && pParent.nCell == 0));

            TRACE("BALANCE: old: %d %d %d  ",
                  apOld[0].pgno,
                  nOld >= 2 ? apOld[1].pgno : 0,
                  nOld >= 3 ? apOld[2].pgno : 0
                );

            /*
      ** Allocate k new pages.  Reuse old pages where possible.
      */
            if (apOld[0].pgno <= 1)
            {
                rc = UnitTest.SQLITE_CORRUPT_BKPT();
                goto balance_cleanup;
            }
            pageFlags = apOld[0].aData[0];
            for (i = 0; i < k; i++)
            {
                var pNew = new MemPage();
                if (i < nOld)
                {
                    pNew = apNew[i] = apOld[i];
                    apOld[i] = null;
                    rc = PagerHelper.PagerWrite(pNew.pDbPage);
                    nNew++;
                    if (rc != 0) goto balance_cleanup;
                }
                else
                {
                    Debug.Assert(i > 0);
                    rc = allocateBtreePage(pBt, ref pNew, ref pgno, pgno, 0);
                    if (rc != 0) goto balance_cleanup;
                    apNew[i] = pNew;
                    nNew++;

                    /* Set the pointer-map entry for the new sibling page. */
#if !SQLITE_OMIT_AUTOVACUUM
                    //   if ( ISAUTOVACUUM )
                    if (pBt.autoVacuum)
#else
if (false)
#endif
                    {
                        ptrmapPut(pBt, pNew.pgno, PTRMAP_BTREE, pParent.pgno, ref rc);
                        if (rc != StatusCode.SQLITE_OK)
                        {
                            goto balance_cleanup;
                        }
                    }
                }
            }

            /* Free any old pages that were not reused as new pages.
      */
            while (i < nOld)
            {
                freePage(apOld[i], ref rc);
                if (rc != 0) goto balance_cleanup;
                releasePage(apOld[i]);
                apOld[i] = null;
                i++;
            }

            /*
      ** Put the new pages in accending order.  This helps to
      ** keep entries in the disk file in order so that a scan
      ** of the table is a linear scan through the file.  That
      ** in turn helps the operating system to deliver pages
      ** from the disk more rapidly.
      **
      ** An O(n^2) insertion sort algorithm is used, but since
      ** n is never more than NB (a small constant), that should
      ** not be a problem.
      **
      ** When NB==3, this one optimization makes the database
      ** about 25% faster for large insertions and deletions.
      */
            for (i = 0; i < k - 1; i++)
            {
                var minV = (int) apNew[i].pgno;
                int minI = i;
                for (j = i + 1; j < k; j++)
                {
                    if (apNew[j].pgno < (uint) minV)
                    {
                        minI = j;
                        minV = (int) apNew[j].pgno;
                    }
                }
                if (minI > i)
                {
                    int t;
                    MemPage pT;
                    t = (int) apNew[i].pgno;
                    pT = apNew[i];
                    apNew[i] = apNew[minI];
                    apNew[minI] = pT;
                }
            }
            TRACE("new: %d(%d) %d(%d) %d(%d) %d(%d) %d(%d)\n",
                  apNew[0].pgno, szNew[0],
                  nNew >= 2 ? apNew[1].pgno : 0, nNew >= 2 ? szNew[1] : 0,
                  nNew >= 3 ? apNew[2].pgno : 0, nNew >= 3 ? szNew[2] : 0,
                  nNew >= 4 ? apNew[3].pgno : 0, nNew >= 4 ? szNew[3] : 0,
                  nNew >= 5 ? apNew[4].pgno : 0, nNew >= 5 ? szNew[4] : 0);

            Debug.Assert(sqlite3PagerIswriteable(pParent.pDbPage));
            Utility.Sqlite3Put4byte(pParent.aData, pRight, apNew[nNew - 1].pgno);

            /*
      ** Evenly distribute the data in apCell[] across the new pages.
      ** Insert divider cells into pParent as necessary.
      */
            j = 0;
            for (i = 0; i < nNew; i++)
            {
                /* Assemble the new sibling page. */
                MemPage pNew = apNew[i];
                Debug.Assert(j < nMaxCells);
                zeroPage(pNew, pageFlags);
                assemblePage(pNew, cntNew[i] - j, apCell, szCell, j);
                Debug.Assert(pNew.nCell > 0 || (nNew == 1 && cntNew[0] == 0));
                Debug.Assert(pNew.nOverflow == 0);

                j = cntNew[i];

                /* If the sibling page assembled above was not the right-most sibling,
        ** insert a divider cell into the parent page.
        */
                Debug.Assert(i < nNew - 1 || j == nCell);
                if (j < nCell)
                {
                    byte[] pCell;
                    byte[] pTemp;
                    int sz;

                    Debug.Assert(j < nMaxCells);
                    pCell = apCell[j];
                    sz = szCell[j] + leafCorrection;
                    pTemp = Malloc.sqlite3Malloc(sz); //&aOvflSpace[iOvflSpace];
                    if (0 == pNew.leaf)
                    {
                        Buffer.BlockCopy(pCell, 0, pNew.aData, 8, 4); //memcpy( pNew.aData[8], pCell, 4 );
                    }
                    else if (leafData != 0)
                    {
                        /* If the tree is a leaf-data tree, and the siblings are leaves,
            ** then there is no divider cell in apCell[]. Instead, the divider
            ** cell consists of the integer key for the right-most cell of
            ** the sibling-page assembled above only.
            */
                        var info = new CellInfo();
                        j--;
                        btreeParseCellPtr(pNew, apCell[j], ref info);
                        pCell = pTemp;
                        sz = 4 + Utility.PutVarint(pCell, 4, (ulong) info.nKey);
                        pTemp = null;
                    }
                    else
                    {
                        //------------ pCell -= 4;
                        byte[] _pCell_4 = Malloc.sqlite3Malloc(pCell.Length + 4);
                        Buffer.BlockCopy(pCell, 0, _pCell_4, 4, pCell.Length);
                        pCell = _pCell_4;
                        //
                        /* Obscure case for non-leaf-data trees: If the cell at pCell was
            ** previously stored on a leaf node, and its reported size was 4
            ** bytes, then it may actually be smaller than this
            ** (see btreeParseCellPtr(), 4 bytes is the minimum size of
            ** any cell). But it is important to pass the correct size to
            ** insertCell(), so reparse the cell now.
            **
            ** Note that this can never happen in an SQLite data file, as all
            ** cells are at least 4 bytes. It only happens in b-trees used
            ** to evaluate "IN (SELECT ...)" and similar clauses.
            */
                        if (szCell[j] == 4)
                        {
                            Debug.Assert(leafCorrection == 4);
                            sz = cellSizePtr(pParent, pCell);
                        }
                    }
                    iOvflSpace += sz;
                    Debug.Assert(sz <= pBt.pageSize/4);
                    Debug.Assert(iOvflSpace <= pBt.pageSize);
                    insertCell(pParent, nxDiv, pCell, sz, pTemp, pNew.pgno, ref rc);
                    if (rc != StatusCode.SQLITE_OK) goto balance_cleanup;
                    Debug.Assert(sqlite3PagerIswriteable(pParent.pDbPage));

                    j++;
                    nxDiv++;
                }
            }
            Debug.Assert(j == nCell);
            Debug.Assert(nOld > 0);
            Debug.Assert(nNew > 0);
            if ((pageFlags & PageTypeFlag.PTF_LEAF) == 0)
            {
                Buffer.BlockCopy(apCopy[nOld - 1].aData, 8, apNew[nNew - 1].aData, 8, 4);
                    //byte* zChild = &apCopy[nOld - 1].aData[8];
                //memcpy( apNew[nNew - 1].aData[8], zChild, 4 );
            }

            if (isRoot != 0 && pParent.nCell == 0 && pParent.hdrOffset <= apNew[0].nFree)
            {
                /* The root page of the b-tree now contains no cells. The only sibling
        ** page is the right-child of the parent. Copy the contents of the
        ** child page into the parent, decreasing the overall height of the
        ** b-tree structure by one. This is described as the "balance-shallower"
        ** sub-algorithm in some documentation.
        **
        ** If this is an auto-vacuum database, the call to copyNodeContent()
        ** sets all pointer-map entries corresponding to database image pages
        ** for which the pointer is stored within the content being copied.
        **
        ** The second Debug.Assert below verifies that the child page is defragmented
        ** (it must be, as it was just reconstructed using assemblePage()). This
        ** is important if the parent page happens to be page 1 of the database
        ** image.  */
                Debug.Assert(nNew == 1);
                Debug.Assert(apNew[0].nFree ==
                             (get2byte(apNew[0].aData, 5) - apNew[0].cellOffset - apNew[0].nCell*2)
                    );
                copyNodeContent(apNew[0], pParent, ref rc);
                freePage(apNew[0], ref rc);
            }
            else
#if !SQLITE_OMIT_AUTOVACUUM
                //   if ( ISAUTOVACUUM )
                if (pBt.autoVacuum)
#else
if (false)
#endif
                {
                    /* Fix the pointer-map entries for all the cells that were shifted around.
          ** There are several different types of pointer-map entries that need to
          ** be dealt with by this routine. Some of these have been set already, but
          ** many have not. The following is a summary:
          **
          **   1) The entries associated with new sibling pages that were not
          **      siblings when this function was called. These have already
          **      been set. We don't need to worry about old siblings that were
          **      moved to the free-list - the freePage() code has taken care
          **      of those.
          **
          **   2) The pointer-map entries associated with the first overflow
          **      page in any overflow chains used by new divider cells. These
          **      have also already been taken care of by the insertCell() code.
          **
          **   3) If the sibling pages are not leaves, then the child pages of
          **      cells stored on the sibling pages may need to be updated.
          **
          **   4) If the sibling pages are not internal intkey nodes, then any
          **      overflow pages used by these cells may need to be updated
          **      (internal intkey nodes never contain pointers to overflow pages).
          **
          **   5) If the sibling pages are not leaves, then the pointer-map
          **      entries for the right-child pages of each sibling may need
          **      to be updated.
          **
          ** Cases 1 and 2 are dealt with above by other code. The next
          ** block deals with cases 3 and 4 and the one after that, case 5. Since
          ** setting a pointer map entry is a relatively expensive operation, this
          ** code only sets pointer map entries for child or overflow pages that have
          ** actually moved between pages.  */
                    MemPage pNew = apNew[0];
                    MemPage pOld = apCopy[0];
                    int nOverflow = pOld.nOverflow;
                    int iNextOld = pOld.nCell + nOverflow;
                    int iOverflow = (nOverflow != 0 ? pOld.aOvfl[0].idx : -1);
                    j = 0; /* Current 'old' sibling page */
                    k = 0; /* Current 'new' sibling page */
                    for (i = 0; i < nCell; i++)
                    {
                        int isDivider = 0;
                        while (i == iNextOld)
                        {
                            /* Cell i is the cell immediately following the last cell on old
              ** sibling page j. If the siblings are not leaf pages of an
              ** intkey b-tree, then cell i was a divider cell. */
                            pOld = apCopy[++j];
                            iNextOld = i + (0 == leafData ? 1 : 0) + pOld.nCell + pOld.nOverflow;
                            if (pOld.nOverflow != 0)
                            {
                                nOverflow = pOld.nOverflow;
                                iOverflow = i + (0 == leafData ? 1 : 0) + pOld.aOvfl[0].idx;
                            }
                            isDivider = 0 == leafData ? 1 : 0;
                        }

                        Debug.Assert(nOverflow > 0 || iOverflow < i);
                        Debug.Assert(nOverflow < 2 || pOld.aOvfl[0].idx == pOld.aOvfl[1].idx - 1);
                        Debug.Assert(nOverflow < 3 || pOld.aOvfl[1].idx == pOld.aOvfl[2].idx - 1);
                        if (i == iOverflow)
                        {
                            isDivider = 1;
                            if ((--nOverflow) > 0)
                            {
                                iOverflow++;
                            }
                        }

                        if (i == cntNew[k])
                        {
                            /* Cell i is the cell immediately following the last cell on new
              ** sibling page k. If the siblings are not leaf pages of an
              ** intkey b-tree, then cell i is a divider cell.  */
                            pNew = apNew[++k];
                            if (0 == leafData) continue;
                        }
                        Debug.Assert(j < nOld);
                        Debug.Assert(k < nNew);

                        /* If the cell was originally divider cell (and is not now) or
            ** an overflow cell, or if the cell was located on a different sibling
            ** page before the balancing, then the pointer map entries associated
            ** with any child or overflow pages need to be updated.  */
                        if (isDivider != 0 || pOld.pgno != pNew.pgno)
                        {
                            if (0 == leafCorrection)
                            {
                                ptrmapPut(pBt, Utility.Sqlite3Get4byte(apCell[i]), PTRMAP_BTREE, pNew.pgno, ref rc);
                            }
                            if (szCell[i] > pNew.minLocal)
                            {
                                ptrmapPutOvflPtr(pNew, apCell[i], ref rc);
                            }
                        }
                    }

                    if (0 == leafCorrection)
                    {
                        for (i = 0; i < nNew; i++)
                        {
                            uint key = Utility.Sqlite3Get4byte(apNew[i].aData, 8);
                            ptrmapPut(pBt, key, PTRMAP_BTREE, apNew[i].pgno, ref rc);
                        }
                    }

#if FALSE
/* The ptrmapCheckPages() contains Debug.Assert() statements that verify that
** all pointer map pages are set correctly. This is helpful while
** debugging. This is usually disabled because a corrupt database may
** cause an Debug.Assert() statement to fail.  */
ptrmapCheckPages(apNew, nNew);
ptrmapCheckPages(pParent, 1);
#endif
                }

            Debug.Assert(pParent.isInit != 0);
            TRACE("BALANCE: finished: old=%d new=%d cells=%d\n",
                  nOld, nNew, nCell);

            /*
    ** Cleanup before returning.
    */
            balance_cleanup:
            Malloc.ScratchFree(apCell);
            for (i = 0; i < nOld; i++)
            {
                releasePage(apOld[i]);
            }
            for (i = 0; i < nNew; i++)
            {
                releasePage(apNew[i]);
            }

            return rc;
        }


        /*
    ** This function is called when the root page of a b-tree structure is
    ** overfull (has one or more overflow pages).
    **
    ** A new child page is allocated and the contents of the current root
    ** page, including overflow cells, are copied into the child. The root
    ** page is then overwritten to make it an empty page with the right-child
    ** pointer pointing to the new page.
    **
    ** Before returning, all pointer-map entries corresponding to pages
    ** that the new child-page now contains pointers to are updated. The
    ** entry corresponding to the new right-child pointer of the root
    ** page is also updated.
    **
    ** If successful, ppChild is set to contain a reference to the child
    ** page and StatusCode.SQLITE_OK is returned. In this case the caller is required
    ** to call releasePage() on ppChild exactly once. If an error occurs,
    ** an error code is returned and ppChild is set to 0.
    */

        private static int balance_deeper(MemPage pRoot, ref MemPage ppChild)
        {
            int rc; /* Return value from subprocedures */
            MemPage pChild = null; /* Pointer to a new child page */
            uint pgnoChild = 0; /* Page number of the new child page */
            BtShared pBt = pRoot.pBt; /* The BTree */

            Debug.Assert(pRoot.nOverflow > 0);
            Debug.Assert(MutexHelper.MutexHeld(pBt.mutex));

            /* Make pRoot, the root page of the b-tree, writable. Allocate a new
      ** page that will become the new right-child of pPage. Copy the contents
      ** of the node stored on pRoot into the new child page.
      */
            rc = PagerHelper.PagerWrite(pRoot.pDbPage);
            if (rc == StatusCode.SQLITE_OK)
            {
                rc = allocateBtreePage(pBt, ref pChild, ref pgnoChild, pRoot.pgno, 0);
                copyNodeContent(pRoot, pChild, ref rc);
#if !SQLITE_OMIT_AUTOVACUUM
                //   if ( ISAUTOVACUUM )
                if (pBt.autoVacuum)
#else
if (false)
#endif
                {
                    ptrmapPut(pBt, pgnoChild, PTRMAP_BTREE, pRoot.pgno, ref rc);
                }
            }
            if (rc != 0)
            {
                ppChild = null;
                releasePage(pChild);
                return rc;
            }
            Debug.Assert(sqlite3PagerIswriteable(pChild.pDbPage));
            Debug.Assert(sqlite3PagerIswriteable(pRoot.pDbPage));
            Debug.Assert(pChild.nCell == pRoot.nCell);

            TRACE("BALANCE: copy root %d into %d\n", pRoot.pgno, pChild.pgno);

            /* Copy the overflow cells from pRoot to pChild */
            Array.Copy(pRoot.aOvfl, pChild.aOvfl, pRoot.nOverflow);
                //memcpy(pChild.aOvfl, pRoot.aOvfl, pRoot.nOverflow*sizeof(pRoot.aOvfl[0]));
            pChild.nOverflow = pRoot.nOverflow;

            /* Zero the contents of pRoot. Then install pChild as the right-child. */
            zeroPage(pRoot, pChild.aData[0] & ~PageTypeFlag.PTF_LEAF);
            Utility.Sqlite3Put4byte(pRoot.aData, pRoot.hdrOffset + 8, pgnoChild);

            ppChild = pChild;
            return StatusCode.SQLITE_OK;
        }

        /*
    ** The page that pCur currently points to has just been modified in
    ** some way. This function figures out if this modification means the
    ** tree needs to be balanced, and if so calls the appropriate balancing
    ** routine. Balancing routines are:
    **
    **   balance_quick()
    **   balance_deeper()
    **   balance_nonroot()
    */
        private static readonly byte[] aBalanceQuickSpace = new byte[13];

        private static int balance(BtCursor pCur)
        {
            int rc = StatusCode.SQLITE_OK;
            int nMin = pCur.pBt.usableSize*2/3;

            //byte[] pFree = null;

#if !NDEBUG || SQLITE_COVERAGE_TEST || DEBUG
            int balance_quick_called = 0; //TESTONLY( int balance_quick_called = 0 );
            int balance_deeper_called = 0; //TESTONLY( int balance_deeper_called = 0 );
#else
int balance_quick_called = 0;
int balance_deeper_called = 0;
#endif

            do
            {
                int iPage = pCur.iPage;
                MemPage pPage = pCur.apPage[iPage];

                if (iPage == 0)
                {
                    if (pPage.nOverflow != 0)
                    {
                        /* The root page of the b-tree is overfull. In this case call the
            ** balance_deeper() function to create a new child for the root-page
            ** and copy the current contents of the root-page to it. The
            ** next iteration of the do-loop will balance the child page.
            */
                        Debug.Assert((balance_deeper_called++) == 0);
                        rc = balance_deeper(pPage, ref pCur.apPage[1]);
                        if (rc == StatusCode.SQLITE_OK)
                        {
                            pCur.iPage = 1;
                            pCur.aiIdx[0] = 0;
                            pCur.aiIdx[1] = 0;
                            Debug.Assert(pCur.apPage[1].nOverflow != 0);
                        }
                    }
                    else
                    {
                        break;
                    }
                }
                else if (pPage.nOverflow == 0 && pPage.nFree <= nMin)
                {
                    break;
                }
                else
                {
                    MemPage pParent = pCur.apPage[iPage - 1];
                    int iIdx = pCur.aiIdx[iPage - 1];

                    rc = PagerHelper.PagerWrite(pParent.pDbPage);
                    if (rc == StatusCode.SQLITE_OK)
                    {
#if !SQLITE_OMIT_QUICKBALANCE
                        if (pPage.hasData != 0
                            && pPage.nOverflow == 1
                            && pPage.aOvfl[0].idx == pPage.nCell
                            && pParent.pgno != 1
                            && pParent.nCell == iIdx
                            )
                        {
                            /* Call balance_quick() to create a new sibling of pPage on which
              ** to store the overflow cell. balance_quick() inserts a new cell
              ** into pParent, which may cause pParent overflow. If this
              ** happens, the next interation of the do-loop will balance pParent
              ** use either balance_nonroot() or balance_deeper(). Until this
              ** happens, the overflow cell is stored in the aBalanceQuickSpace[]
              ** buffer.
              **
              ** The purpose of the following Debug.Assert() is to check that only a
              ** single call to balance_quick() is made for each call to this
              ** function. If this were not verified, a subtle bug involving reuse
              ** of the aBalanceQuickSpace[] might sneak in.
              */
                            Debug.Assert((balance_quick_called++) == 0);
                            rc = balance_quick(pParent, pPage, aBalanceQuickSpace);
                        }
                        else
#endif
                        {
                            /* In this case, call balance_nonroot() to redistribute cells
              ** between pPage and up to 2 of its sibling pages. This involves
              ** modifying the contents of pParent, which may cause pParent to
              ** become overfull or underfull. The next iteration of the do-loop
              ** will balance the parent page to correct this.
              **
              ** If the parent page becomes overfull, the overflow cell or cells
              ** are stored in the pSpace buffer allocated immediately below.
              ** A subsequent iteration of the do-loop will deal with this by
              ** calling balance_nonroot() (balance_deeper() may be called first,
              ** but it doesn't deal with overflow cells - just moves them to a
              ** different page). Once this subsequent call to balance_nonroot()
              ** has completed, it is safe to release the pSpace buffer used by
              ** the previous call, as the overflow cell data will have been
              ** copied either into the body of a database page or into the new
              ** pSpace buffer passed to the latter call to balance_nonroot().
              */
                            //byte[] pSpace = new byte[pCur.pBt.pageSize];// byte pSpace = sqlite3PageMalloc( pCur.pBt.pageSize );
                            rc = balance_nonroot(pParent, iIdx, null, iPage == 1 ? 1 : 0);
                            //if (pFree != null)
                            //{
                            //  /* If pFree is not NULL, it points to the pSpace buffer used
                            //  ** by a previous call to balance_nonroot(). Its contents are
                            //  ** now stored either on real database pages or within the
                            //  ** new pSpace buffer, so it may be safely freed here. */
                            //  sqlite3PageFree(ref pFree);
                            //}

                            /* The pSpace buffer will be freed after the next call to
              ** balance_nonroot(), or just before this function returns, whichever
              ** comes first. */
                            //pFree = pSpace;
                        }
                    }

                    pPage.nOverflow = 0;

                    /* The next iteration of the do-loop balances the parent page. */
                    releasePage(pPage);
                    pCur.iPage--;
                }
            } while (rc == StatusCode.SQLITE_OK);

            //if (pFree != null)
            //{
            //  sqlite3PageFree(ref pFree);
            //}
            return rc;
        }


        /*
    ** Insert a new record into the BTree.  The key is given by (pKey,nKey)
    ** and the data is given by (pData,nData).  The cursor is used only to
    ** define what table the record should be inserted into.  The cursor
    ** is left pointing at a random location.
    **
    ** For an INTKEY table, only the nKey value of the key is used.  pKey is
    ** ignored.  For a ZERODATA table, the pData and nData are both ignored.
    **
    ** If the seekResult parameter is non-zero, then a successful call to
    ** MovetoUnpacked() to seek cursor pCur to (pKey, nKey) has already
    ** been performed. seekResult is the search result returned (a negative
    ** number if pCur points at an entry that is smaller than (pKey, nKey), or
    ** a positive value if pCur points at an etry that is larger than
    ** (pKey, nKey)).
    **
    ** If the seekResult parameter is non-zero, then the caller guarantees that
    ** cursor pCur is pointing at the existing copy of a row that is to be
    ** overwritten.  If the seekResult parameter is 0, then cursor pCur may
    ** point to any entry or to no entry at all and so this function has to seek
    ** the cursor before the new key can be inserted.
    */

        public static int BtreeInsert(
            BtCursor pCur, /* Insert data into the table of this cursor */
            byte[] pKey, long nKey, /* The key of the new record */
            byte[] pData, int nData, /* The data of the new record */
            int nZero, /* Number of extra 0 bytes to append to data */
            int appendBias, /* True if this is Utility.Likely an append */
            int seekResult /* Result of prior MovetoUnpacked() call */
            )
        {
            int rc;
            int loc = seekResult; /* -1: before desired location  +1: after */
            int szNew = 0;
            int idx;
            MemPage pPage;
            Btree p = pCur.pBtree;
            BtShared pBt = p.pBt;
            int oldCell;
            byte[] newCell = null;

            if (pCur.eState == CursorFlag.CURSOR_FAULT)
            {
                Debug.Assert(pCur.skipNext != StatusCode.SQLITE_OK);
                return pCur.skipNext;
            }

            Debug.Assert(cursorHoldsMutex(pCur));
            Debug.Assert(pCur.wrFlag != 0 && pBt.inTransaction == TransactionType.TRANS_WRITE && !pBt.readOnly);
            Debug.Assert(hasSharedCacheTableLock(p, pCur.pgnoRoot, pCur.pKeyInfo != null ? 1 : 0, 2));

            /* Assert that the caller has been consistent. If this cursor was opened
      ** expecting an index b-tree, then the caller should be inserting blob
      ** keys with no associated data. If the cursor was opened expecting an
      ** intkey table, the caller should be inserting integer keys with a
      ** blob of associated data.  */
            Debug.Assert((pKey == null) == (pCur.pKeyInfo == null));

            /* If this is an insert into a table b-tree, invalidate any incrblob
      ** cursors open on the row being replaced (assuming this is a replace
      ** operation - if it is not, the following is a no-op).  */
            if (pCur.pKeyInfo == null)
            {
                invalidateIncrblobCursors(p, nKey, 0);
            }

            /* Save the positions of any other cursors open on this table.
      **
      ** In some cases, the call to btreeMoveto() below is a no-op. For
      ** example, when inserting data into a table with auto-generated integer
      ** keys, the VDBE layer invokes BTreeHelper.BtreeLast() to figure out the
      ** integer key to use. It then calls this function to actually insert the
      ** data into the intkey B-Tree. In this case btreeMoveto() recognizes
      ** that the cursor is already where it needs to be and returns without
      ** doing any work. To avoid thwarting these optimizations, it is important
      ** not to clear the cursor here.
      */
            rc = saveAllCursors(pBt, pCur.pgnoRoot, pCur);
            if (rc != 0) return rc;
            if (0 == loc)
            {
                rc = btreeMoveto(pCur, pKey, nKey, appendBias, ref loc);
                if (rc != 0) return rc;
            }
            Debug.Assert(pCur.eState == CursorFlag.CURSOR_VALID || (pCur.eState == CursorFlag.CURSOR_INVALID && loc != 0));

            pPage = pCur.apPage[pCur.iPage];
            Debug.Assert(pPage.intKey != 0 || nKey >= 0);
            Debug.Assert(pPage.leaf != 0 || 0 == pPage.intKey);

            TRACE("INSERT: table=%d nkey=%lld ndata=%d page=%d %s\n",
                  pCur.pgnoRoot, nKey, nData, pPage.pgno,
                  loc == 0 ? "overwrite" : "new entry");
            Debug.Assert(pPage.isInit != 0);
            allocateTempSpace(pBt);
            newCell = pBt.pTmpSpace;
            //if (newCell == null) return StatusCode.SQLITE_NOMEM;
            rc = fillInCell(pPage, newCell, pKey, nKey, pData, nData, nZero, ref szNew);
            if (rc != 0) goto end_insert;
            Debug.Assert(szNew == cellSizePtr(pPage, newCell));
            Debug.Assert(szNew <= MX_CELL_SIZE(pBt));
            idx = pCur.aiIdx[pCur.iPage];
            if (loc == 0)
            {
                ushort szOld;
                Debug.Assert(idx < pPage.nCell);
                rc = PagerHelper.PagerWrite(pPage.pDbPage);
                if (rc != 0)
                {
                    goto end_insert;
                }
                oldCell = findCell(pPage, idx);
                if (0 == pPage.leaf)
                {
                    //memcpy(newCell, oldCell, 4);
                    newCell[0] = pPage.aData[oldCell + 0];
                    newCell[1] = pPage.aData[oldCell + 1];
                    newCell[2] = pPage.aData[oldCell + 2];
                    newCell[3] = pPage.aData[oldCell + 3];
                }
                szOld = cellSizePtr(pPage, oldCell);
                rc = clearCell(pPage, oldCell);
                dropCell(pPage, idx, szOld, ref rc);
                if (rc != 0) goto end_insert;
            }
            else if (loc < 0 && pPage.nCell > 0)
            {
                Debug.Assert(pPage.leaf != 0);
                idx = ++pCur.aiIdx[pCur.iPage];
            }
            else
            {
                Debug.Assert(pPage.leaf != 0);
            }
            insertCell(pPage, idx, newCell, szNew, null, 0, ref rc);
            Debug.Assert(rc != StatusCode.SQLITE_OK || pPage.nCell > 0 || pPage.nOverflow > 0);

            /* If no error has occured and pPage has an overflow cell, call balance()
      ** to redistribute the cells within the tree. Since balance() may move
      ** the cursor, zero the BtCursor.info.nSize and BtCursor.validNKey
      ** variables.
      **
      ** Previous versions of SQLite called moveToRoot() to move the cursor
      ** back to the root page as balance() used to invalidate the contents
      ** of BtCursor.apPage[] and BtCursor.aiIdx[]. Instead of doing that,
      ** set the cursor state to "invalid". This makes common insert operations
      ** slightly faster.
      **
      ** There is a subtle but important optimization here too. When inserting
      ** multiple records into an intkey b-tree using a single cursor (as can
      ** happen while processing an "INSERT INTO ... SELECT" statement), it
      ** is advantageous to leave the cursor pointing to the last entry in
      ** the b-tree if possible. If the cursor is left pointing to the last
      ** entry in the table, and the next row inserted has an integer key
      ** larger than the largest existing key, it is possible to insert the
      ** row without seeking the cursor. This can be a big performance boost.
      */
            pCur.info.nSize = 0;
            pCur.validNKey = false;
            if (rc == StatusCode.SQLITE_OK && pPage.nOverflow != 0)
            {
                rc = balance(pCur);

                /* Must make sure nOverflow is reset to zero even if the balance()
        ** fails. Internal data structure corruption will result otherwise.
        ** Also, set the cursor state to invalid. This stops saveCursorPosition()
        ** from trying to save the current position of the cursor.  */
                pCur.apPage[pCur.iPage].nOverflow = 0;
                pCur.eState = CursorFlag.CURSOR_INVALID;
            }
            Debug.Assert(pCur.apPage[pCur.iPage].nOverflow == 0);

            end_insert:
            return rc;
        }

        /*
    ** Delete the entry that the cursor is pointing to.  The cursor
    ** is left pointing at a arbitrary location.
    */

        public static int BtreeDelete(BtCursor pCur)
        {
            Btree p = pCur.pBtree;
            BtShared pBt = p.pBt;
            int rc; /* Return code */
            MemPage pPage; /* Page to delete cell from */
            int pCell; /* Pointer to cell to delete */
            int iCellIdx; /* Index of cell to delete */
            int iCellDepth; /* Depth of node containing pCell */

            Debug.Assert(cursorHoldsMutex(pCur));
            Debug.Assert(pBt.inTransaction == TransactionType.TRANS_WRITE);
            Debug.Assert(!pBt.readOnly);
            Debug.Assert(pCur.wrFlag != 0);
            Debug.Assert(hasSharedCacheTableLock(p, pCur.pgnoRoot, pCur.pKeyInfo != null ? 1 : 0, 2));
            Debug.Assert(!hasReadConflicts(p, pCur.pgnoRoot));

            if (UnitTest.NEVER(pCur.aiIdx[pCur.iPage] >= pCur.apPage[pCur.iPage].nCell)
                || UnitTest.NEVER(pCur.eState != CursorFlag.CURSOR_VALID)
                )
            {
                return StatusCode.SQLITE_ERROR; /* Something has gone awry. */
            }

            /* If this is a delete operation to remove a row from a table b-tree,
      ** invalidate any incrblob cursors open on the row being deleted.  */
            if (pCur.pKeyInfo == null)
            {
                invalidateIncrblobCursors(p, pCur.info.nKey, 0);
            }

            iCellDepth = pCur.iPage;
            iCellIdx = pCur.aiIdx[iCellDepth];
            pPage = pCur.apPage[iCellDepth];
            pCell = findCell(pPage, iCellIdx);

            /* If the page containing the entry to delete is not a leaf page, move
      ** the cursor to the largest entry in the tree that is smaller than
      ** the entry being deleted. This cell will replace the cell being deleted
      ** from the internal node. The 'previous' entry is used for this instead
      ** of the 'next' entry, as the previous entry is always a part of the
      ** sub-tree headed by the child page of the cell being deleted. This makes
      ** balancing the tree following the delete operation easier.  */
            if (0 == pPage.leaf)
            {
                int notUsed = 0;
                rc = BtreePrevious(pCur, ref notUsed);
                if (rc != 0) return rc;
            }

            /* Save the positions of any other cursors open on this table before
      ** making any modifications. Make the page containing the entry to be
      ** deleted writable. Then free any overflow pages associated with the
      ** entry and finally remove the cell itself from within the page.
      */
            rc = saveAllCursors(pBt, pCur.pgnoRoot, pCur);
            if (rc != 0) return rc;
            rc = PagerHelper.PagerWrite(pPage.pDbPage);
            if (rc != 0) return rc;
            rc = clearCell(pPage, pCell);
            dropCell(pPage, iCellIdx, cellSizePtr(pPage, pCell), ref rc);
            if (rc != 0) return rc;

            /* If the cell deleted was not located on a leaf page, then the cursor
      ** is currently pointing to the largest entry in the sub-tree headed
      ** by the child-page of the cell that was just deleted from an internal
      ** node. The cell from the leaf node needs to be moved to the internal
      ** node to replace the deleted cell.  */
            if (0 == pPage.leaf)
            {
                MemPage pLeaf = pCur.apPage[pCur.iPage];
                int nCell;
                uint n = pCur.apPage[iCellDepth + 1].pgno;
                //byte[] pTmp;

                pCell = findCell(pLeaf, pLeaf.nCell - 1);
                nCell = cellSizePtr(pLeaf, pCell);
                Debug.Assert(MX_CELL_SIZE(pBt) >= nCell);

                //allocateTempSpace(pBt);
                //pTmp = pBt.pTmpSpace;

                rc = PagerHelper.PagerWrite(pLeaf.pDbPage);
                byte[] pNext_4 = Malloc.sqlite3Malloc(nCell + 4);
                Buffer.BlockCopy(pLeaf.aData, pCell - 4, pNext_4, 0, nCell + 4);
                insertCell(pPage, iCellIdx, pNext_4, nCell + 4, null, n, ref rc);
                    //insertCell( pPage, iCellIdx, pCell - 4, nCell + 4, pTmp, n, ref rc );
                dropCell(pLeaf, pLeaf.nCell - 1, nCell, ref rc);
                if (rc != 0) return rc;
            }

            /* Balance the tree. If the entry deleted was located on a leaf page,
      ** then the cursor still points to that page. In this case the first
      ** call to balance() repairs the tree, and the if(...) condition is
      ** never true.
      **
      ** Otherwise, if the entry deleted was on an internal node page, then
      ** pCur is pointing to the leaf page from which a cell was removed to
      ** replace the cell deleted from the internal node. This is slightly
      ** tricky as the leaf node may be underfull, and the internal node may
      ** be either under or overfull. In this case run the balancing algorithm
      ** on the leaf node first. If the balance proceeds far enough up the
      ** tree that we can be sure that any problem in the internal node has
      ** been corrected, so be it. Otherwise, after balancing the leaf node,
      ** walk the cursor up the tree to the internal node and balance it as
      ** well.  */
            rc = balance(pCur);
            if (rc == StatusCode.SQLITE_OK && pCur.iPage > iCellDepth)
            {
                while (pCur.iPage > iCellDepth)
                {
                    releasePage(pCur.apPage[pCur.iPage--]);
                }
                rc = balance(pCur);
            }

            if (rc == StatusCode.SQLITE_OK)
            {
                moveToRoot(pCur);
            }
            return rc;
        }

        /*
    ** Create a new BTree table.  Write into piTable the page
    ** number for the root page of the new table.
    **
    ** The type of type is determined by the flags parameter.  Only the
    ** following values of flags are currently in use.  Other values for
    ** flags might not work:
    **
    **     CreateTableFlag.BTREE_INTKEY|CreateTableFlag.BTREE_LEAFDATA     Used for SQL tables with rowid keys
    **     CreateTableFlag.BTREE_ZERODATA                  Used for SQL indices
    */

        private static int btreeCreateTable(Btree p, ref int piTable, int flags)
        {
            BtShared pBt = p.pBt;
            var pRoot = new MemPage();
            uint pgnoRoot = 0;
            int rc;

            Debug.Assert(sqlite3BtreeHoldsMutex(p));
            Debug.Assert(pBt.inTransaction == TransactionType.TRANS_WRITE);
            Debug.Assert(!pBt.readOnly);

#if SQLITE_OMIT_AUTOVACUUM
rc = allocateBtreePage(pBt, ref pRoot, ref pgnoRoot, 1, 0);
if( rc !=0){
return rc;
}
#else
            if (pBt.autoVacuum)
            {
                uint pgnoMove = 0; /* Move a page here to make room for the root-page */
                var pPageMove = new MemPage(); /* The page to move to. */

                /* Creating a new table may probably require moving an existing database
        ** to make room for the new tables root page. In case this page turns
        ** out to be an overflow page, delete all overflow page-map caches
        ** held by open cursors.
        */
                invalidateAllOverflowCache(pBt);

                /* Read the value of meta[3] from the database to determine where the
        ** root page of the new table should go. meta[3] is the largest root-page
        ** created so far, so the new root-page is (meta[3]+1).
        */
                sqlite3BtreeGetMeta(p, BtreeMeta.BTREE_LARGEST_ROOT_PAGE, ref pgnoRoot);
                pgnoRoot++;

                /* The new root-page may not be allocated on a pointer-map page, or the
        ** Global.PENDING_BYTE page.
        */
                while (pgnoRoot == PTRMAP_PAGENO(pBt, pgnoRoot) ||
                       pgnoRoot == Utility.PENDING_BYTE_PAGE(pBt))
                {
                    pgnoRoot++;
                }
                Debug.Assert(pgnoRoot >= 3);

                /* Allocate a page. The page that currently resides at pgnoRoot will
        ** be moved to the allocated page (unless the allocated page happens
        ** to reside at pgnoRoot).
        */
                rc = allocateBtreePage(pBt, ref pPageMove, ref pgnoMove, pgnoRoot, 1);
                if (rc != StatusCode.SQLITE_OK)
                {
                    return rc;
                }

                if (pgnoMove != pgnoRoot)
                {
                    /* pgnoRoot is the page that will be used for the root-page of
          ** the new table (assuming an error did not occur). But we were
          ** allocated pgnoMove. If required (i.e. if it was not allocated
          ** by extending the file), the current page at position pgnoMove
          ** is already journaled.
          */
                    byte eType = 0;
                    uint iPtrPage = 0;

                    releasePage(pPageMove);

                    /* Move the page currently at pgnoRoot to pgnoMove. */
                    rc = btreeGetPage(pBt, pgnoRoot, ref pRoot, 0);
                    if (rc != StatusCode.SQLITE_OK)
                    {
                        return rc;
                    }
                    rc = ptrmapGet(pBt, pgnoRoot, ref eType, ref iPtrPage);
                    if (eType == PTRMAP_ROOTPAGE || eType == PTRMAP_FREEPAGE)
                    {
                        rc = UnitTest.SQLITE_CORRUPT_BKPT();
                    }
                    if (rc != StatusCode.SQLITE_OK)
                    {
                        releasePage(pRoot);
                        return rc;
                    }
                    Debug.Assert(eType != PTRMAP_ROOTPAGE);
                    Debug.Assert(eType != PTRMAP_FREEPAGE);
                    rc = relocatePage(pBt, pRoot, eType, iPtrPage, pgnoMove, 0);
                    releasePage(pRoot);

                    /* Obtain the page at pgnoRoot */
                    if (rc != StatusCode.SQLITE_OK)
                    {
                        return rc;
                    }
                    rc = btreeGetPage(pBt, pgnoRoot, ref pRoot, 0);
                    if (rc != StatusCode.SQLITE_OK)
                    {
                        return rc;
                    }
                    rc = PagerHelper.PagerWrite(pRoot.pDbPage);
                    if (rc != StatusCode.SQLITE_OK)
                    {
                        releasePage(pRoot);
                        return rc;
                    }
                }
                else
                {
                    pRoot = pPageMove;
                }

                /* Update the pointer-map and meta-data with the new root-page number. */
                ptrmapPut(pBt, pgnoRoot, PTRMAP_ROOTPAGE, 0, ref rc);
                if (rc != 0)
                {
                    releasePage(pRoot);
                    return rc;
                }
                rc = sqlite3BtreeUpdateMeta(p, 4, pgnoRoot);
                if (rc != 0)
                {
                    releasePage(pRoot);
                    return rc;
                }
            }
            else
            {
                rc = allocateBtreePage(pBt, ref pRoot, ref pgnoRoot, 1, 0);
                if (rc != 0) return rc;
            }
#endif
            Debug.Assert(sqlite3PagerIswriteable(pRoot.pDbPage));
            zeroPage(pRoot, flags | PageTypeFlag.PTF_LEAF);
            sqlite3PagerUnref(pRoot.pDbPage);
            piTable = (int) pgnoRoot;
            return StatusCode.SQLITE_OK;
        }

        public static int BtreeCreateTable(Btree p, ref int piTable, int flags)
        {
            int rc;
            sqlite3BtreeEnter(p);
            rc = btreeCreateTable(p, ref piTable, flags);
            sqlite3BtreeLeave(p);
            return rc;
        }

        /*
    ** Erase the given database page and all its children.  Return
    ** the page to the freelist.
    */

        private static int clearDatabasePage(
            BtShared pBt, /* The BTree that contains the table */
            uint pgno, /* Page number to clear */
            int freePageFlag, /* Deallocate page if true */
            ref int pnChange /* Add number of Cells freed to this counter */
            )
        {
            var pPage = new MemPage();
            int rc;
            byte[] pCell;
            int i;

            Debug.Assert(MutexHelper.MutexHeld(pBt.mutex));
            if (pgno > pagerPagecount(pBt))
            {
                return UnitTest.SQLITE_CORRUPT_BKPT();
            }

            rc = getAndInitPage(pBt, pgno, ref pPage);
            if (rc != 0) return rc;
            for (i = 0; i < pPage.nCell; i++)
            {
                int iCell = findCell(pPage, i);
                pCell = pPage.aData; //        pCell = findCell( pPage, i );
                if (0 == pPage.leaf)
                {
                    rc = clearDatabasePage(pBt, Utility.Sqlite3Get4byte(pCell, iCell), 1, ref pnChange);
                    if (rc != 0) goto cleardatabasepage_out;
                }
                rc = clearCell(pPage, iCell);
                if (rc != 0) goto cleardatabasepage_out;
            }
            if (0 == pPage.leaf)
            {
                rc = clearDatabasePage(pBt, Utility.Sqlite3Get4byte(pPage.aData, 8), 1, ref pnChange);
                if (rc != 0) goto cleardatabasepage_out;
            }
            else //if (pnChange != 0)
            {
                //Debug.Assert(pPage.intKey != 0);
                pnChange += pPage.nCell;
            }
            if (freePageFlag != 0)
            {
                freePage(pPage, ref rc);
            }
            else if ((rc = PagerHelper.PagerWrite(pPage.pDbPage)) == 0)
            {
                zeroPage(pPage, pPage.aData[0] | PageTypeFlag.PTF_LEAF);
            }

            cleardatabasepage_out:
            releasePage(pPage);
            return rc;
        }

        /*
    ** Delete all information from a single table in the database.  iTable is
    ** the page number of the root of the table.  After this routine returns,
    ** the root page is empty, but still exists.
    **
    ** This routine will fail with StatusCode.SQLITE_LOCKED if there are any open
    ** read cursors on the table.  Open write cursors are moved to the
    ** root of the table.
    **
    ** If pnChange is not NULL, then table iTable must be an intkey table. The
    ** integer value pointed to by pnChange is incremented by the number of
    ** entries in the table.
    */

        public static int BtreeClearTable(Btree p, int iTable, ref int pnChange)
        {
            int rc;
            BtShared pBt = p.pBt;
            sqlite3BtreeEnter(p);
            Debug.Assert(p.inTrans == TransactionType.TRANS_WRITE);

            /* Invalidate all incrblob cursors open on table iTable (assuming iTable
      ** is the root of a table b-tree - if it is not, the following call is
      ** a no-op).  */
            invalidateIncrblobCursors(p, 0, 1);

            rc = saveAllCursors(pBt, (uint) iTable, null);
            if (StatusCode.SQLITE_OK == rc)
            {
                rc = clearDatabasePage(pBt, (uint) iTable, 0, ref pnChange);
            }
            sqlite3BtreeLeave(p);
            return rc;
        }

        /*
    ** Erase all information in a table and add the root of the table to
    ** the freelist.  Except, the root of the principle table (the one on
    ** page 1) is never added to the freelist.
    **
    ** This routine will fail with StatusCode.SQLITE_LOCKED if there are any open
    ** cursors on the table.
    **
    ** If AUTOVACUUM is enabled and the page at iTable is not the last
    ** root page in the database file, then the last root page
    ** in the database file is moved into the slot formerly occupied by
    ** iTable and that last slot formerly occupied by the last root page
    ** is added to the freelist instead of iTable.  In this say, all
    ** root pages are kept at the beginning of the database file, which
    ** is necessary for AUTOVACUUM to work right.  piMoved is set to the
    ** page number that used to be the last root page in the file before
    ** the move.  If no page gets moved, piMoved is set to 0.
    ** The last root page is recorded in meta[3] and the value of
    ** meta[3] is updated by this procedure.
    */

        private static int btreeDropTable(Btree p, uint iTable, ref int piMoved)
        {
            int rc;
            MemPage pPage = null;
            BtShared pBt = p.pBt;

            Debug.Assert(sqlite3BtreeHoldsMutex(p));
            Debug.Assert(p.inTrans == TransactionType.TRANS_WRITE);

            /* It is illegal to drop a table if any cursors are open on the
      ** database. This is because in auto-vacuum mode the backend may
      ** need to move another root-page to fill a gap left by the deleted
      ** root page. If an open cursor was using this page a problem would
      ** occur.
      **
      ** This error is caught long before control reaches this point.
      */
            if (UnitTest.NEVER(pBt.pCursor))
            {
                sqlite3ConnectionBlocked(p.db, pBt.pCursor.pBtree.db);
                return ExtendedResultCode.SQLITE_LOCKED_SHAREDCACHE;
            }

            rc = btreeGetPage(pBt, iTable, ref pPage, 0);
            if (rc != 0) return rc;
            int Dummy0 = 0;
            rc = BtreeClearTable(p, (int) iTable, ref Dummy0);
            if (rc != 0)
            {
                releasePage(pPage);
                return rc;
            }

            piMoved = 0;

            if (iTable > 1)
            {
#if SQLITE_OMIT_AUTOVACUUM
freePage(pPage, ref rc);
releasePage(pPage);
#else
                if (pBt.autoVacuum)
                {
                    uint maxRootuint = 0;
                    sqlite3BtreeGetMeta(p, BtreeMeta.BTREE_LARGEST_ROOT_PAGE, ref maxRootuint);

                    if (iTable == maxRootuint)
                    {
                        /* If the table being dropped is the table with the largest root-page
            ** number in the database, put the root page on the free list.
            */
                        freePage(pPage, ref rc);
                        releasePage(pPage);
                        if (rc != StatusCode.SQLITE_OK)
                        {
                            return rc;
                        }
                    }
                    else
                    {
                        /* The table being dropped does not have the largest root-page
            ** number in the database. So move the page that does into the
            ** gap left by the deleted root-page.
            */
                        var pMove = new MemPage();
                        releasePage(pPage);
                        rc = btreeGetPage(pBt, maxRootuint, ref pMove, 0);
                        if (rc != StatusCode.SQLITE_OK)
                        {
                            return rc;
                        }
                        rc = relocatePage(pBt, pMove, PTRMAP_ROOTPAGE, 0, iTable, 0);
                        releasePage(pMove);
                        if (rc != StatusCode.SQLITE_OK)
                        {
                            return rc;
                        }
                        pMove = null;
                        rc = btreeGetPage(pBt, maxRootuint, ref pMove, 0);
                        freePage(pMove, ref rc);
                        releasePage(pMove);
                        if (rc != StatusCode.SQLITE_OK)
                        {
                            return rc;
                        }
                        piMoved = (int) maxRootuint;
                    }

                    /* Set the new 'max-root-page' value in the database header. This
          ** is the old value less one, less one more if that happens to
          ** be a root-page number, less one again if that is the
          ** Utility.PENDING_BYTE_PAGE.
          */
                    maxRootuint--;
                    while (maxRootuint == Utility.PENDING_BYTE_PAGE(pBt)
                           || PTRMAP_ISPAGE(pBt, maxRootuint))
                    {
                        maxRootuint--;
                    }
                    Debug.Assert(maxRootuint != Utility.PENDING_BYTE_PAGE(pBt));

                    rc = sqlite3BtreeUpdateMeta(p, 4, maxRootuint);
                }
                else
                {
                    freePage(pPage, ref rc);
                    releasePage(pPage);
                }
#endif
            }
            else
            {
                /* If BTreeHelper.BtreeDropTable was called on page 1.
        ** This really never should happen except in a corrupt
        ** database.
        */
                zeroPage(pPage, PageTypeFlag.PTF_INTKEY | PageTypeFlag.PTF_LEAF);
                releasePage(pPage);
            }
            return rc;
        }

        public static int BtreeDropTable(Btree p, int iTable, ref int piMoved)
        {
            int rc;
            sqlite3BtreeEnter(p);
            rc = btreeDropTable(p, (uint) iTable, ref piMoved);
            sqlite3BtreeLeave(p);
            return rc;
        }


        /*
    ** This function may only be called if the b-tree connection already
    ** has a read or write transaction open on the database.
    **
    ** Read the meta-information out of a database file.  Meta[0]
    ** is the number of free pages currently in the database.  Meta[1]
    ** through meta[15] are available for use by higher layers.  Meta[0]
    ** is read-only, the others are read/write.
    **
    ** The schema layer numbers meta values differently.  At the schema
    ** layer (and the SetCookie and ReadCookie opcodes) the number of
    ** free pages is not visible.  So Cookie[0] is the same as Meta[1].
    */

        private static void sqlite3BtreeGetMeta(Btree p, int idx, ref uint pMeta)
        {
            BtShared pBt = p.pBt;

            sqlite3BtreeEnter(p);
            Debug.Assert(p.inTrans > TransactionType.TRANS_NONE);
            Debug.Assert(StatusCode.SQLITE_OK == querySharedCacheTableLock(p, Const.MASTER_ROOT, LockType.READ_LOCK));
            Debug.Assert(pBt.pPage1 != null);
            Debug.Assert(idx >= 0 && idx <= 15);

            pMeta = Utility.Sqlite3Get4byte(pBt.pPage1.aData, 36 + idx*4);

            /* If auto-vacuum is disabled in this build and this is an auto-vacuum
      ** database, mark the database as read-only.  */
#if SQLITE_OMIT_AUTOVACUUM
if( idx==BtreeMeta.BTREE_LARGEST_ROOT_PAGE && pMeta>0 ) pBt.readOnly = 1;
#endif

            sqlite3BtreeLeave(p);
        }

        /*
    ** Write meta-information back into the database.  Meta[0] is
    ** read-only and may not be written.
    */

        private static int sqlite3BtreeUpdateMeta(Btree p, int idx, uint iMeta)
        {
            BtShared pBt = p.pBt;
            byte[] pP1;
            int rc;
            Debug.Assert(idx >= 1 && idx <= 15);
            sqlite3BtreeEnter(p);
            Debug.Assert(p.inTrans == TransactionType.TRANS_WRITE);
            Debug.Assert(pBt.pPage1 != null);
            pP1 = pBt.pPage1.aData;
            rc = PagerHelper.PagerWrite(pBt.pPage1.pDbPage);
            if (rc == StatusCode.SQLITE_OK)
            {
                Utility.Sqlite3Put4byte(pP1, 36 + idx*4, iMeta);
#if !SQLITE_OMIT_AUTOVACUUM
                if (idx == BtreeMeta.BTREE_INCR_VACUUM)
                {
                    Debug.Assert(pBt.autoVacuum || iMeta == 0);
                    Debug.Assert(iMeta == 0 || iMeta == 1);
                    pBt.incrVacuum = iMeta != 0;
                }
#endif
            }
            sqlite3BtreeLeave(p);
            return rc;
        }

#if !SQLITE_OMIT_BTREECOUNT
        /*
** The first argument, pCur, is a cursor opened on some b-tree. Count the
** number of entries in the b-tree and write the result to pnEntry.
**
** StatusCode.SQLITE_OK is returned if the operation is successfully executed.
** Otherwise, if an error is encountered (i.e. an IO error or database
** corruption) an SQLite error code is returned.
*/

        private static int sqlite3BtreeCount(BtCursor pCur, ref long pnEntry)
        {
            long nEntry = 0; /* Value to return in pnEntry */
            int rc; /* Return code */
            rc = moveToRoot(pCur);

            /* Unless an error occurs, the following loop runs one iteration for each
      ** page in the B-Tree structure (not including overflow pages).
      */
            while (rc == StatusCode.SQLITE_OK)
            {
                int iIdx; /* Index of child node in parent */
                MemPage pPage; /* Current page of the b-tree */

                /* If this is a leaf page or the tree is not an int-key tree, then
        ** this page contains countable entries. Increment the entry counter
        ** accordingly.
        */
                pPage = pCur.apPage[pCur.iPage];
                if (pPage.leaf != 0 || 0 == pPage.intKey)
                {
                    nEntry += pPage.nCell;
                }

                /* pPage is a leaf node. This loop navigates the cursor so that it
        ** points to the first interior cell that it points to the parent of
        ** the next page in the tree that has not yet been visited. The
        ** pCur.aiIdx[pCur.iPage] value is set to the index of the parent cell
        ** of the page, or to the number of cells in the page if the next page
        ** to visit is the right-child of its parent.
        **
        ** If all pages in the tree have been visited, return StatusCode.SQLITE_OK to the
        ** caller.
        */
                if (pPage.leaf != 0)
                {
                    do
                    {
                        if (pCur.iPage == 0)
                        {
                            /* All pages of the b-tree have been visited. Return successfully. */
                            pnEntry = nEntry;
                            return StatusCode.SQLITE_OK;
                        }
                        moveToParent(pCur);
                    } while (pCur.aiIdx[pCur.iPage] >= pCur.apPage[pCur.iPage].nCell);

                    pCur.aiIdx[pCur.iPage]++;
                    pPage = pCur.apPage[pCur.iPage];
                }

                /* Descend to the child node of the cell that the cursor currently
        ** points at. This is the right-child if (iIdx==pPage.nCell).
        */
                iIdx = pCur.aiIdx[pCur.iPage];
                if (iIdx == pPage.nCell)
                {
                    rc = moveToChild(pCur, Utility.Sqlite3Get4byte(pPage.aData, pPage.hdrOffset + 8));
                }
                else
                {
                    rc = moveToChild(pCur, Utility.Sqlite3Get4byte(pPage.aData, findCell(pPage, iIdx)));
                }
            }

            /* An error has occurred. Return an error code. */
            return rc;
        }
#endif

        /*
** Return the pager associated with a BTree.  This routine is used for
** testing and debugging only.
*/

        private static Pager sqlite3BtreePager(Btree p)
        {
            return p.pBt.pPager;
        }

#if !SQLITE_OMIT_INTEGRITY_CHECK
        /*
** Append a message to the error message string.
*/

        private static void checkAppendMsg(
            IntegrityCk pCheck,
            string zMsg1,
            string zFormat,
            params object[] ap
            )
        {
            //va_list ap;
            if (0 == pCheck.mxErr) return;
            pCheck.mxErr--;
            pCheck.nErr++;
            Custom.VaStart(ap, zFormat);
            if (pCheck.errMsg.zText.Length != 0)
            {
                Print.StrAccumAppend(pCheck.errMsg, "\n", 1);
            }
            if (!String.IsNullOrEmpty(zMsg1))
            {
                Print.StrAccumAppend(pCheck.errMsg, zMsg1, -1);
            }
            Print.VXPrintf(pCheck.errMsg, 1, zFormat, ap);
            Custom.VaEnd(ap);
            //if( pCheck.errMsg.mallocFailed ){
            //  pCheck.mallocFailed = 1;
            //}
        }
#endif
        //* SQLITE_OMIT_INTEGRITY_CHECK */

#if !SQLITE_OMIT_INTEGRITY_CHECK
        /*
** Add 1 to the reference count for page iPage.  If this is the second
** reference to the page, add an error message to pCheck.zErrMsg.
** Return 1 if there are 2 ore more references to the page and 0 if
** if this is the first reference to the page.
**
** Also check that the page number is in bounds.
*/

        private static int checkRef(IntegrityCk pCheck, uint iPage, string zContext)
        {
            if (iPage == 0) return 1;
            if (iPage > pCheck.nPage)
            {
                checkAppendMsg(pCheck, zContext, "invalid page number %d", iPage);
                return 1;
            }
            if (pCheck.anRef[iPage] == 1)
            {
                checkAppendMsg(pCheck, zContext, "2nd reference to page %d", iPage);
                return 1;
            }
            return ((pCheck.anRef[iPage]++) > 1) ? 1 : 0;
        }

#if !SQLITE_OMIT_AUTOVACUUM
        /*
** Check that the entry in the pointer-map for page iChild maps to
** page iParent, pointer type ptrType. If not, append an error message
** to pCheck.
*/

        private static void checkPtrmap(
            IntegrityCk pCheck, /* Integrity check context */
            uint iChild, /* Child page number */
            byte eType, /* Expected pointer map type */
            uint iParent, /* Expected pointer map parent page number */
            string zContext /* Context description (used for error msg) */
            )
        {
            int rc;
            byte ePtrmapType = 0;
            uint iPtrmapParent = 0;

            rc = ptrmapGet(pCheck.pBt, iChild, ref ePtrmapType, ref iPtrmapParent);
            if (rc != StatusCode.SQLITE_OK)
            {
                //if( rc==StatusCode.SQLITE_NOMEM || rc==ExtendedResultCode.SQLITE_IOERR_NOMEM ) pCheck.mallocFailed = 1;
                checkAppendMsg(pCheck, zContext, "Failed to read ptrmap key=%d", iChild);
                return;
            }

            if (ePtrmapType != eType || iPtrmapParent != iParent)
            {
                checkAppendMsg(pCheck, zContext,
                               "Bad ptr map entry key=%d expected=(%d,%d) got=(%d,%d)",
                               iChild, eType, iParent, ePtrmapType, iPtrmapParent);
            }
        }
#endif

        /*
** Check the integrity of the freelist or of an overflow page list.
** Verify that the number of pages on the list is N.
*/

        private static void checkList(
            IntegrityCk pCheck, /* Integrity checking context */
            int isFreeList, /* True for a freelist.  False for overflow page list */
            int iPage, /* Page number for first page in the list */
            int N, /* Expected number of pages in the list */
            string zContext /* Context for error messages */
            )
        {
            int i;
            int expected = N;
            int iFirst = iPage;
            while (N-- > 0 && pCheck.mxErr != 0)
            {
                var pOvflPage = new PgHdr();
                byte[] pOvflData;
                if (iPage < 1)
                {
                    checkAppendMsg(pCheck, zContext,
                                   "%d of %d pages missing from overflow list starting at %d",
                                   N + 1, expected, iFirst);
                    break;
                }
                if (checkRef(pCheck, (uint) iPage, zContext) != 0) break;
                if (sqlite3PagerGet(pCheck.pPager, (uint) iPage, ref pOvflPage) != 0)
                {
                    checkAppendMsg(pCheck, zContext, "failed to get page %d", iPage);
                    break;
                }
                pOvflData = sqlite3PagerGetData(pOvflPage);
                if (isFreeList != 0)
                {
                    var n = (int) Utility.Sqlite3Get4byte(pOvflData, 4);
#if !SQLITE_OMIT_AUTOVACUUM
                    if (pCheck.pBt.autoVacuum)
                    {
                        checkPtrmap(pCheck, (uint) iPage, PTRMAP_FREEPAGE, 0, zContext);
                    }
#endif
                    if (n > pCheck.pBt.usableSize/4 - 2)
                    {
                        checkAppendMsg(pCheck, zContext,
                                       "freelist leaf count too big on page %d", iPage);
                        N--;
                    }
                    else
                    {
                        for (i = 0; i < n; i++)
                        {
                            uint iFreePage = Utility.Sqlite3Get4byte(pOvflData, 8 + i*4);
#if !SQLITE_OMIT_AUTOVACUUM
                            if (pCheck.pBt.autoVacuum)
                            {
                                checkPtrmap(pCheck, iFreePage, PTRMAP_FREEPAGE, 0, zContext);
                            }
#endif
                            checkRef(pCheck, iFreePage, zContext);
                        }
                        N -= n;
                    }
                }
#if !SQLITE_OMIT_AUTOVACUUM
                else
                {
                    /* If this database supports auto-vacuum and iPage is not the last
          ** page in this overflow list, check that the pointer-map entry for
          ** the following page matches iPage.
          */
                    if (pCheck.pBt.autoVacuum && N > 0)
                    {
                        i = (int) Utility.Sqlite3Get4byte(pOvflData);
                        checkPtrmap(pCheck, (uint) i, PTRMAP_OVERFLOW2, (uint) iPage, zContext);
                    }
                }
#endif
                iPage = (int) Utility.Sqlite3Get4byte(pOvflData);
                sqlite3PagerUnref(pOvflPage);
            }
        }
#endif
        //* SQLITE_OMIT_INTEGRITY_CHECK */

#if !SQLITE_OMIT_INTEGRITY_CHECK
        /*
** Do various sanity checks on a single page of a tree.  Return
** the tree depth.  Root pages return 0.  Parents of root pages
** return 1, and so forth.
**
** These checks are done:
**
**      1.  Make sure that cells and freeblocks do not overlap
**          but combine to completely cover the page.
**  NO  2.  Make sure cell keys are in order.
**  NO  3.  Make sure no key is less than or equal to zLowerBound.
**  NO  4.  Make sure no key is greater than or equal to zUpperBound.
**      5.  Check the integrity of overflow pages.
**      6.  Recursively call checkTreePage on all children.
**      7.  Verify that the depth of all children is the same.
**      8.  Make sure this page is at least 33% full or else it is
**          the root of the tree.
*/

        private static long refNULL; //Dummy for C# ref NULL

        private static int checkTreePage(
            IntegrityCk pCheck, /* Context for the sanity check */
            int iPage, /* Page number of the page to check */
            string zParentContext, /* Parent context */
            ref long pnParentMinKey,
            ref long pnParentMaxKey,
            object _pnParentMinKey, /* C# Needed to determine if content passed*/
            object _pnParentMaxKey /* C# Needed to determine if content passed*/
            )
        {
            var pPage = new MemPage();
            int i, rc, depth, d2, pgno, cnt;
            int hdr, cellStart;
            int nCell;
            byte[] data;
            BtShared pBt;
            int usableSize;
            string zContext = ""; //[100];
            byte[] hit = null;
            long nMinKey = 0;
            long nMaxKey = 0;


            Print.Snprintf(200, ref zContext, "Page %d: ", iPage);

            /* Check that the page exists
      */
            pBt = pCheck.pBt;
            usableSize = pBt.usableSize;
            if (iPage == 0) return 0;
            if (checkRef(pCheck, (uint) iPage, zParentContext) != 0) return 0;
            if ((rc = btreeGetPage(pBt, (uint) iPage, ref pPage, 0)) != 0)
            {
                checkAppendMsg(pCheck, zContext,
                               "unable to get the page. error code=%d", rc);
                return 0;
            }

            /* Clear MemPage.isInit to make sure the corruption detection code in
      ** btreeInitPage() is executed.  */
            pPage.isInit = 0;
            if ((rc = btreeInitPage(pPage)) != 0)
            {
                Debug.Assert(rc == StatusCode.SQLITE_CORRUPT); /* The only possible error from InitPage */
                checkAppendMsg(pCheck, zContext,
                               "btreeInitPage() returns error code %d", rc);
                releasePage(pPage);
                return 0;
            }

            /* Check out all the cells.
      */
            depth = 0;
            for (i = 0; i < pPage.nCell && pCheck.mxErr != 0; i++)
            {
                byte[] pCell;
                uint sz;
                var info = new CellInfo();

                /* Check payload overflow pages
        */
                Print.Snprintf(200, ref zContext,
                                 "On tree page %d cell %d: ", iPage, i);
                int iCell = findCell(pPage, i); //pCell = findCell( pPage, i );
                pCell = pPage.aData;
                btreeParseCellPtr(pPage, iCell, ref info); //btreeParseCellPtr( pPage, pCell, info );
                sz = info.nData;
                if (0 == pPage.intKey) sz += (uint) info.nKey;
                    /* For intKey pages, check that the keys are in order.
        */
                else if (i == 0) nMinKey = nMaxKey = info.nKey;
                else
                {
                    if (info.nKey <= nMaxKey)
                    {
                        checkAppendMsg(pCheck, zContext,
                                       "Rowid %lld out of order (previous was %lld)", info.nKey, nMaxKey);
                    }
                    nMaxKey = info.nKey;
                }
                Debug.Assert(sz == info.nPayload);
                if ((sz > info.nLocal)
                    //&& (pCell[info.iOverflow]<=&pPage.aData[pBt.usableSize])
                    )
                {
                    int nPage = (int) (sz - info.nLocal + usableSize - 5)/(usableSize - 4);
                    uint pgnoOvfl = Utility.Sqlite3Get4byte(pCell, iCell, info.iOverflow);
#if !SQLITE_OMIT_AUTOVACUUM
                    if (pBt.autoVacuum)
                    {
                        checkPtrmap(pCheck, pgnoOvfl, PTRMAP_OVERFLOW1, (uint) iPage, zContext);
                    }
#endif
                    checkList(pCheck, 0, (int) pgnoOvfl, nPage, zContext);
                }

                /* Check sanity of left child page.
        */
                if (0 == pPage.leaf)
                {
                    pgno = (int) Utility.Sqlite3Get4byte(pCell, iCell); //Utility.Sqlite3Get4byte( pCell );
#if !SQLITE_OMIT_AUTOVACUUM
                    if (pBt.autoVacuum)
                    {
                        checkPtrmap(pCheck, (uint) pgno, PTRMAP_BTREE, (uint) iPage, zContext);
                    }
#endif
                    if (i == 0)
                        d2 = checkTreePage(pCheck, pgno, zContext, ref nMinKey, ref refNULL, pCheck, null);
                    else
                        d2 = checkTreePage(pCheck, pgno, zContext, ref nMinKey, ref nMaxKey, pCheck, pCheck);

                    if (i > 0 && d2 != depth)
                    {
                        checkAppendMsg(pCheck, zContext, "Child page depth differs");
                    }
                    depth = d2;
                }
            }
            if (0 == pPage.leaf)
            {
                pgno = (int) Utility.Sqlite3Get4byte(pPage.aData, pPage.hdrOffset + 8);
                Print.Snprintf(200, ref zContext,
                                 "On page %d at right child: ", iPage);
#if !SQLITE_OMIT_AUTOVACUUM
                if (pBt.autoVacuum)
                {
                    checkPtrmap(pCheck, (uint) pgno, PTRMAP_BTREE, (uint) iPage, zContext);
                }
#endif
                //    checkTreePage(pCheck, pgno, zContext, NULL, !pPage->nCell ? NULL : &nMaxKey);
                if (0 == pPage.nCell)
                    checkTreePage(pCheck, pgno, zContext, ref refNULL, ref refNULL, null, null);
                else
                    checkTreePage(pCheck, pgno, zContext, ref refNULL, ref nMaxKey, null, pCheck);
            }

            /* For intKey leaf pages, check that the min/max keys are in order
      ** with any left/parent/right pages.
      */
            if (pPage.leaf != 0 && pPage.intKey != 0)
            {
                /* if we are a left child page */
                if (_pnParentMinKey != null)
                {
                    /* if we are the left most child page */
                    if (_pnParentMaxKey == null)
                    {
                        if (nMaxKey > pnParentMinKey)
                        {
                            checkAppendMsg(pCheck, zContext,
                                           "Rowid %lld out of order (max larger than parent min of %lld)",
                                           nMaxKey, pnParentMinKey);
                        }
                    }
                    else
                    {
                        if (nMinKey <= pnParentMinKey)
                        {
                            checkAppendMsg(pCheck, zContext,
                                           "Rowid %lld out of order (min less than parent min of %lld)",
                                           nMinKey, pnParentMinKey);
                        }
                        if (nMaxKey > pnParentMaxKey)
                        {
                            checkAppendMsg(pCheck, zContext,
                                           "Rowid %lld out of order (max larger than parent max of %lld)",
                                           nMaxKey, pnParentMaxKey);
                        }
                        pnParentMinKey = nMaxKey;
                    }
                    /* else if we're a right child page */
                }
                else if (_pnParentMaxKey != null)
                {
                    if (nMinKey <= pnParentMaxKey)
                    {
                        checkAppendMsg(pCheck, zContext,
                                       "Rowid %lld out of order (min less than parent max of %lld)",
                                       nMinKey, pnParentMaxKey);
                    }
                }
            }

            /* Check for complete coverage of the page
      */
            data = pPage.aData;
            hdr = pPage.hdrOffset;
            hit = Malloc.sqlite3Malloc(pBt.pageSize);
            //if( hit==null ){
            //  pCheck.mallocFailed = 1;
            //}else
            {
                var contentOffset = (ushort) get2byte(data, hdr + 5);
                Debug.Assert(contentOffset <= usableSize); /* Enforced by btreeInitPage() */
                Array.Clear(hit, contentOffset, usableSize - contentOffset);
                    //memset(hit+contentOffset, 0, usableSize-contentOffset);
                for (int iLoop = contentOffset - 1; iLoop >= 0; iLoop--)
                    hit[iLoop] = 1; //memset(hit, 1, contentOffset);
                nCell = get2byte(data, hdr + 3);
                cellStart = hdr + 12 - 4*pPage.leaf;
                for (i = 0; i < nCell; i++)
                {
                    int pc = get2byte(data, cellStart + i*2);
                    ushort size = 1024;
                    int j;
                    if (pc <= usableSize - 4)
                    {
                        size = cellSizePtr(pPage, data, pc);
                    }
                    if ((pc + size - 1) >= usableSize)
                    {
                        checkAppendMsg(pCheck, null,
                                       "Corruption detected in cell %d on page %d", i, iPage);
                    }
                    else
                    {
                        for (j = pc + size - 1; j >= pc; j--) hit[j]++;
                    }
                }
                i = get2byte(data, hdr + 1);
                while (i > 0)
                {
                    int size, j;
                    Debug.Assert(i <= usableSize - 4); /* Enforced by btreeInitPage() */
                    size = get2byte(data, i + 2);
                    Debug.Assert(i + size <= usableSize); /* Enforced by btreeInitPage() */
                    for (j = i + size - 1; j >= i; j--) hit[j]++;
                    j = get2byte(data, i);
                    Debug.Assert(j == 0 || j > i + size); /* Enforced by btreeInitPage() */
                    Debug.Assert(j <= usableSize - 4); /* Enforced by btreeInitPage() */
                    i = j;
                }
                for (i = cnt = 0; i < usableSize; i++)
                {
                    if (hit[i] == 0)
                    {
                        cnt++;
                    }
                    else if (hit[i] > 1)
                    {
                        checkAppendMsg(pCheck, "",
                                       "Multiple uses for byte %d of page %d", i, iPage);
                        break;
                    }
                }
                if (cnt != data[hdr + 7])
                {
                    checkAppendMsg(pCheck, null,
                                   "Fragmentation of %d bytes reported as %d on page %d",
                                   cnt, data[hdr + 7], iPage);
                }
            }
            sqlite3PageFree(ref hit);
            releasePage(pPage);
            return depth + 1;
        }
#endif
        //* SQLITE_OMIT_INTEGRITY_CHECK */

#if !SQLITE_OMIT_INTEGRITY_CHECK
        /*
** This routine does a complete check of the given BTree file.  aRoot[] is
** an array of pages numbers were each page number is the root page of
** a table.  nRoot is the number of entries in aRoot.
**
** A read-only or read-write transaction must be opened before calling
** this function.
**
** Write the number of error seen in pnErr.  Except for some memory
** allocation errors,  an error message held in memory obtained from
** malloc is returned if pnErr is non-zero.  If pnErr==null then NULL is
** returned.  If a memory allocation error occurs, NULL is returned.
*/

        private static string sqlite3BtreeIntegrityCheck(
            Btree p, /* The btree to be checked */
            int[] aRoot, /* An array of root pages numbers for individual trees */
            int nRoot, /* Number of entries in aRoot[] */
            int mxErr, /* Stop reporting errors after this many */
            ref int pnErr /* Write number of errors seen to this variable */
            )
        {
            uint i;
            int nRef;
            var sCheck = new IntegrityCk();
            BtShared pBt = p.pBt;
            //StringBuilder zErr = new StringBuilder( 100 );//char zErr[100];

            sqlite3BtreeEnter(p);
            Debug.Assert(p.inTrans > TransactionType.TRANS_NONE && pBt.inTransaction > TransactionType.TRANS_NONE);
            nRef = sqlite3PagerRefcount(pBt.pPager);
            sCheck.pBt = pBt;
            sCheck.pPager = pBt.pPager;
            sCheck.nPage = pagerPagecount(sCheck.pBt);
            sCheck.mxErr = mxErr;
            sCheck.nErr = 0;
            //sCheck.mallocFailed = 0;
            pnErr = 0;
            if (sCheck.nPage == 0)
            {
                sqlite3BtreeLeave(p);
                return "";
            }
            sCheck.anRef = Malloc.sqlite3Malloc(sCheck.anRef, (int) sCheck.nPage + 1);
            //if( !sCheck.anRef ){
            //  pnErr = 1;
            //  sqlite3BtreeLeave(p);
            //  return 0;
            //}
            // for (i = 0; i <= sCheck.nPage; i++) { sCheck.anRef[i] = 0; }
            i = Utility.PENDING_BYTE_PAGE(pBt);
            if (i <= sCheck.nPage)
            {
                sCheck.anRef[i] = 1;
            }
            Print.StrAccumInit(sCheck.errMsg, null, 1000, 20000);

            /* Check the integrity of the freelist
      */
            checkList(sCheck, 1, (int) Utility.Sqlite3Get4byte(pBt.pPage1.aData, 32),
                      (int) Utility.Sqlite3Get4byte(pBt.pPage1.aData, 36), "Main freelist: ");

            /* Check all the tables.
      */
            for (i = 0; (int) i < nRoot && sCheck.mxErr != 0; i++)
            {
                if (aRoot[i] == 0) continue;
#if !SQLITE_OMIT_AUTOVACUUM
                if (pBt.autoVacuum && aRoot[i] > 1)
                {
                    checkPtrmap(sCheck, (uint) aRoot[i], PTRMAP_ROOTPAGE, 0, "");
                }
#endif
                checkTreePage(sCheck, aRoot[i], "List of tree roots: ", ref refNULL, ref refNULL, null, null);
            }

            /* Make sure every page in the file is referenced
      */
            for (i = 1; i <= sCheck.nPage && sCheck.mxErr != 0; i++)
            {
#if SQLITE_OMIT_AUTOVACUUM
if( sCheck.anRef[i]==null ){
checkAppendMsg(sCheck, 0, "Page %d is never used", i);
}
#else
                /* If the database supports auto-vacuum, make sure no tables contain
** references to pointer-map pages.
*/
                if (sCheck.anRef[i] == 0 &&
                    (PTRMAP_PAGENO(pBt, i) != i || !pBt.autoVacuum))
                {
                    checkAppendMsg(sCheck, null, "Page %d is never used", i);
                }
                if (sCheck.anRef[i] != 0 &&
                    (PTRMAP_PAGENO(pBt, i) == i && pBt.autoVacuum))
                {
                    checkAppendMsg(sCheck, null, "Pointer map page %d is referenced", i);
                }
#endif
            }

            /* Make sure this analysis did not leave any unref() pages.
      ** This is an internal consistency check; an integrity check
      ** of the integrity check.
      */
            if (UnitTest.NEVER(nRef != sqlite3PagerRefcount(pBt.pPager)))
            {
                checkAppendMsg(sCheck, null,
                               "Outstanding page count goes from %d to %d during this analysis",
                               nRef, sqlite3PagerRefcount(pBt.pPager)
                    );
            }

            /* Clean  up and report errors.
      */
            sqlite3BtreeLeave(p);
            sCheck.anRef = null; // Malloc.sqlite3_free( ref sCheck.anRef );
            //if( sCheck.mallocFailed ){
            //  sqlite3StrAccumReset(sCheck.errMsg);
            //  pnErr = sCheck.nErr+1;
            //  return 0;
            //}
            pnErr = sCheck.nErr;
            if (sCheck.nErr == 0) Print.StrAccumReset(sCheck.errMsg);
            return Print.StrAccumFinish(sCheck.errMsg);
        }
#endif
        //* SQLITE_OMIT_INTEGRITY_CHECK */

        /*
** Return the full pathname of the underlying database file.
**
** The pager filename is invariant as long as the pager is
** open so it is safe to access without the BtShared mutex.
*/

        private static string sqlite3BtreeGetFilename(Btree p)
        {
            Debug.Assert(p.pBt.pPager != null);
            return sqlite3PagerFilename(p.pBt.pPager);
        }

        /*
    ** Return the pathname of the journal file for this database. The return
    ** value of this routine is the same regardless of whether the journal file
    ** has been created or not.
    **
    ** The pager journal filename is invariant as long as the pager is
    ** open so it is safe to access without the BtShared mutex.
    */

        private static string sqlite3BtreeGetJournalname(Btree p)
        {
            Debug.Assert(p.pBt.pPager != null);
            return sqlite3PagerJournalname(p.pBt.pPager);
        }

        /*
    ** Return non-zero if a transaction is active.
    */

        private static bool sqlite3BtreeIsInTrans(Btree p)
        {
            Debug.Assert(p == null || MutexHelper.MutexHeld(p.db.mutex));
            return (p != null && (p.inTrans == TransactionType.TRANS_WRITE));
        }

        /*
    ** Return non-zero if a read (or write) transaction is active.
    */

        private static bool sqlite3BtreeIsInReadTrans(Btree p)
        {
            Debug.Assert(p != null);
            Debug.Assert(MutexHelper.MutexHeld(p.db.mutex));
            return p.inTrans != TransactionType.TRANS_NONE;
        }

        private static bool sqlite3BtreeIsInBackup(Btree p)
        {
            Debug.Assert(p != null);
            Debug.Assert(MutexHelper.MutexHeld(p.db.mutex));
            return p.nBackup != 0;
        }

        /*
    ** This function returns a pointer to a blob of memory associated with
    ** a single shared-btree. The memory is used by client code for its own
    ** purposes (for example, to store a high-level schema associated with
    ** the shared-btree). The btree layer manages reference counting issues.
    **
    ** The first time this is called on a shared-btree, nBytes bytes of memory
    ** are allocated, zeroed, and returned to the caller. For each subsequent
    ** call the nBytes parameter is ignored and a pointer to the same blob
    ** of memory returned.
    **
    ** If the nBytes parameter is 0 and the blob of memory has not yet been
    ** allocated, a null pointer is returned. If the blob has already been
    ** allocated, it is returned as normal.
    **
    ** Just before the shared-btree is closed, the function passed as the
    ** xFree argument when the memory allocation was made is invoked on the
    ** blob of allocated memory. This function should not call Malloc.sqlite3_free(ref )
    ** on the memory, the btree layer does that.
    */

        private static Schema sqlite3BtreeSchema(Btree p, int nBytes, dxFreeSchema xFree)
        {
            BtShared pBt = p.pBt;
            sqlite3BtreeEnter(p);
            if (null == pBt.pSchema && nBytes != 0)
            {
                pBt.pSchema = new Schema(); //Malloc.MallocZero(nBytes);
                pBt.xFreeSchema = xFree;
            }
            sqlite3BtreeLeave(p);
            return pBt.pSchema;
        }

        /*
    ** Return ExtendedResultCode.SQLITE_LOCKED_SHAREDCACHE if another user of the same shared
    ** btree as the argument handle holds an exclusive lock on the
    ** sqlite_master table. Otherwise StatusCode.SQLITE_OK.
    */

        private static int sqlite3BtreeSchemaLocked(Btree p)
        {
            int rc;
            Debug.Assert(MutexHelper.MutexHeld(p.db.mutex));
            sqlite3BtreeEnter(p);
            rc = querySharedCacheTableLock(p, Const.MASTER_ROOT, LockType.READ_LOCK);
            Debug.Assert(rc == StatusCode.SQLITE_OK || rc == ExtendedResultCode.SQLITE_LOCKED_SHAREDCACHE);
            sqlite3BtreeLeave(p);
            return rc;
        }


#if !SQLITE_OMIT_SHARED_CACHE
/*
** Obtain a lock on the table whose root page is iTab.  The
** lock is a write lock if isWritelock is true or a read lock
** if it is false.
*/
int sqlite3BtreeLockTable(Btree p, int iTab, byte isWriteLock){
int rc = StatusCode.SQLITE_OK;
Debug.Assert( p.inTrans!=TransactionType.TRANS_NONE );
if( p.sharable ){
byte lockType = LockType.READ_LOCK + isWriteLock;
Debug.Assert( LockType.READ_LOCK+1==LockType.WRITE_LOCK );
Debug.Assert( isWriteLock==null || isWriteLock==1 );

sqlite3BtreeEnter(p);
rc = querySharedCacheTableLock(p, iTab, lockType);
if( rc==StatusCode.SQLITE_OK ){
rc = setSharedCacheTableLock(p, iTab, lockType);
}
sqlite3BtreeLeave(p);
}
return rc;
}
#endif

#if !SQLITE_OMIT_INCRBLOB
/*
** Argument pCsr must be a cursor opened for writing on an
** INTKEY table currently pointing at a valid table entry.
** This function modifies the data stored as part of that entry.
**
** Only the data content may only be modified, it is not possible to
** change the length of the data stored. If this function is called with
** parameters that attempt to write past the end of the existing data,
** no modifications are made and StatusCode.SQLITE_CORRUPT is returned.
*/
int sqlite3BtreePutData(BtCursor pCsr, uint offset, uint amt, void *z){
int rc;
Debug.Assert( cursorHoldsMutex(pCsr) );
Debug.Assert( MutexHelper.MutexHeld(pCsr.pBtree.db.mutex) );
Debug.Assert( pCsr.isIncrblobHandle );

rc = restoreCursorPosition(pCsr);
if( rc!=StatusCode.SQLITE_OK ){
return rc;
}
Debug.Assert( pCsr.eState!=CursorFlag.CURSOR_REQUIRESEEK );
if( pCsr.eState!=CursorFlag.CURSOR_VALID ){
return StatusCode.SQLITE_ABORT;
}

/* Check some assumptions:
**   (a) the cursor is open for writing,
**   (b) there is a read/write transaction open,
**   (c) the connection holds a write-lock on the table (if required),
**   (d) there are no conflicting read-locks, and
**   (e) the cursor points at a valid row of an intKey table.
*/
if( !pCsr.wrFlag ){
return StatusCode.SQLITE_READONLY;
}
Debug.Assert( !pCsr.pBt.readOnly && pCsr.pBt.inTransaction==TransactionType.TRANS_WRITE );
Debug.Assert( hasSharedCacheTableLock(pCsr.pBtree, pCsr.pgnoRoot, 0, 2) );
Debug.Assert( !hasReadConflicts(pCsr.pBtree, pCsr.pgnoRoot) );
Debug.Assert( pCsr.apPage[pCsr.iPage].intKey );

return accessPayload(pCsr, offset, amt, (byte[] *)z, 1);
}

/*
** Set a flag on this cursor to cache the locations of pages from the
** overflow list for the current row. This is used by cursors opened
** for incremental blob IO only.
**
** This function sets a flag only. The actual page location cache
** (stored in BtCursor.aOverflow[]) is allocated and used by function
** accessPayload() (the worker function for BTreeHelper.BtreeData() and
** sqlite3BtreePutData()).
*/
void sqlite3BtreeCacheOverflow(BtCursor pCur){
Debug.Assert( cursorHoldsMutex(pCur) );
Debug.Assert( MutexHelper.MutexHeld(pCur.pBtree.db.mutex) );
Debug.Assert(!pCur.isIncrblobHandle);
Debug.Assert(!pCur.aOverflow);
pCur.isIncrblobHandle = 1;
}
#endif
    }
}